diff options
author | Robert Godfrey <rgodfrey@apache.org> | 2012-03-01 12:51:40 +0000 |
---|---|---|
committer | Robert Godfrey <rgodfrey@apache.org> | 2012-03-01 12:51:40 +0000 |
commit | 1f50bcb47f363c92f5f194fa55713e6c967a1449 (patch) | |
tree | d09de1f68f99d72772389d57e260ef7cf6c966f6 | |
parent | e1126b978d046384380eeaa5a9b309618ddc7df5 (diff) | |
download | qpid-python-1f50bcb47f363c92f5f194fa55713e6c967a1449.tar.gz |
NO-JIRA: [AMQP 1-0 Sandbox] merging from trunk
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rg-amqp-1-0-sandbox@1295541 13f79535-47bb-0310-9956-ffa450edef68
351 files changed, 27906 insertions, 9237 deletions
diff --git a/qpid/java/bdbstore/bin/backup.sh b/qpid/java/bdbstore/bin/backup.sh index 22d8d52b58..b58ab16282 100755 --- a/qpid/java/bdbstore/bin/backup.sh +++ b/qpid/java/bdbstore/bin/backup.sh @@ -33,7 +33,7 @@ if [ -z "$QPID_HOME" ]; then fi VERSION=0.15 -LIBS=$QPID_HOME/lib/je-4.0.103.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar +LIBS=$QPID_HOME/lib/opt/je-4.0.117.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar echo "Starting Hot Backup Script" diff --git a/qpid/java/bdbstore/bin/storeUpgrade.sh b/qpid/java/bdbstore/bin/storeUpgrade.sh index f3b089b3d3..ffb33f7fbd 100755 --- a/qpid/java/bdbstore/bin/storeUpgrade.sh +++ b/qpid/java/bdbstore/bin/storeUpgrade.sh @@ -32,12 +32,8 @@ if [ -z "$QPID_HOME" ]; then export PATH=${PATH}:${QPID_HOME}/bin fi -if [ -z "$BDB_HOME" ]; then - export BDB_HOME=$(dirname $(dirname $(readlink -f $0))) -fi - VERSION=0.15 -LIBS=$BDB_HOME/lib/je-4.0.103.jar:$BDB_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar +LIBS=$QPID_HOME/lib/opt/je-4.0.117.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar:$QPID_HOME/lib/qpid-all.jar java -Xms256m -Dlog4j.configuration=BDBStoreUpgrade.log4j.xml -Xmx256m -Damqj.logging.level=warn ${JAVA_OPTS} -cp $LIBS org.apache.qpid.server.store.berkeleydb.BDBStoreUpgrade ${ARGS} diff --git a/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh b/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh index 2a0b72e5ad..4224f98de2 100755 --- a/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh +++ b/qpid/java/bdbstore/etc/scripts/bdbbackuptest.sh @@ -32,10 +32,10 @@ for arg in "$@"; do fi done -VERSION=0.5 +VERSION=0.15 # Set classpath to include Qpid jar with all required jars in manifest -QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.4.0.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.6.1.jar:$QPID_HOME/lib/qpid-bdbstore-$VERSION.jar # Set other variables used by the qpid-run script before calling export JAVA=java JAVA_MEM=-Xmx256m QPID_CLASSPATH=$QPID_LIBS diff --git a/qpid/java/bdbstore/etc/scripts/bdbtest.sh b/qpid/java/bdbstore/etc/scripts/bdbtest.sh index eafdae9710..d53481c393 100755 --- a/qpid/java/bdbstore/etc/scripts/bdbtest.sh +++ b/qpid/java/bdbstore/etc/scripts/bdbtest.sh @@ -32,10 +32,10 @@ for arg in "$@"; do fi done -VERSION=0.5 +VERSION=0.15 # Set classpath to include Qpid jar with all required jars in manifest -QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.4.0.jar +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/qpid-junit-toolkit-$VERSION.jar:$QPID_HOME/lib/junit-3.8.1.jar:$QPID_HOME/lib/log4j-1.2.12.jar:$QPID_HOME/lib/qpid-systests-$VERSION.jar:$QPID_HOME/lib/qpid-perftests-$VERSION.jar:$QPID_HOME/lib/slf4j-log4j12-1.6.1.jar # Set other variables used by the qpid-run script before calling export JAVA=java JAVA_MEM=-Xmx256m QPID_CLASSPATH=$QPID_LIBS diff --git a/qpid/java/bdbstore/src/resources/backup-log4j.xml b/qpid/java/bdbstore/src/main/java/backup-log4j.xml index 6b0619f0b6..6b0619f0b6 100644 --- a/qpid/java/bdbstore/src/resources/backup-log4j.xml +++ b/qpid/java/bdbstore/src/main/java/backup-log4j.xml diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java index 4861e007af..8e55e79e01 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBUpgradeTest.java @@ -94,13 +94,6 @@ public class BDBUpgradeTest extends QpidBrokerTestCase assertNotNull("QPID_WORK must be set", QPID_WORK_ORIG); assertNotNull("QPID_HOME must be set", QPID_HOME); - if(! isExternalBroker()) - { - //override QPID_WORK to add the InVM port used so the store - //output from the upgrade tool can be found by the broker - setSystemProperty("QPID_WORK", QPID_WORK_ORIG + "/" + getPort()); - } - _fromDir = QPID_HOME + "/bdbstore-to-upgrade/test-store"; _toDir = getWorkDirBaseDir() + "/bdbstore/test-store"; _toDirTwice = getWorkDirBaseDir() + "/bdbstore-upgraded-twice"; diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java index 78355a7501..402b991419 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.security.access.config; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; @@ -36,7 +37,7 @@ import javax.security.auth.Subject; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; -import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.log4j.Logger; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.security.Result; import org.apache.qpid.server.security.access.ObjectProperties; @@ -53,20 +54,15 @@ import org.apache.qpid.server.security.access.logging.AccessControlMessages; */ public class RuleSet { + public static final Logger _logger = Logger.getLogger(RuleSet.class); + private static final String AT = "@"; private static final String SLASH = "/"; public static final String DEFAULT_ALLOW = "defaultallow"; public static final String DEFAULT_DENY = "defaultdeny"; - public static final String TRANSITIVE = "transitive"; - public static final String EXPAND = "expand"; - public static final String AUTONUMBER = "autonumber"; - public static final String CONTROLLED = "controlled"; - public static final String VALIDATE = "validate"; - public static final List<String> CONFIG_PROPERTIES = Arrays.asList( - DEFAULT_ALLOW, DEFAULT_DENY, TRANSITIVE, EXPAND, AUTONUMBER, CONTROLLED - ); + public static final List<String> CONFIG_PROPERTIES = Arrays.asList(DEFAULT_ALLOW, DEFAULT_DENY); private static final Integer _increment = 10; @@ -80,7 +76,6 @@ public class RuleSet { // set some default configuration properties configure(DEFAULT_DENY, Boolean.TRUE); - configure(TRANSITIVE, Boolean.TRUE); } /** @@ -139,12 +134,21 @@ public class RuleSet // Save the rules we selected objects.put(objectType, filtered); + if(_logger.isDebugEnabled()) + { + _logger.debug("Cached " + objectType + " RulesList: " + filtered); + } } // Return the cached rules - return objects.get(objectType); - } + List<Rule> rules = objects.get(objectType); + if(_logger.isDebugEnabled()) + { + _logger.debug("Returning RuleList: " + rules); + } + return rules; + } public boolean isValidNumber(Integer number) { @@ -175,20 +179,6 @@ public class RuleSet return false; } - private Permission noLog(Permission permission) - { - switch (permission) - { - case ALLOW: - case ALLOW_LOG: - return Permission.ALLOW; - case DENY: - case DENY_LOG: - default: - return Permission.DENY; - } - } - // TODO make this work when group membership is not known at file parse time public void addRule(Integer number, String identity, Permission permission, Action action) { @@ -196,63 +186,13 @@ public class RuleSet if (!action.isAllowed()) { - throw new IllegalArgumentException("Action is not allowd: " + action); + throw new IllegalArgumentException("Action is not allowed: " + action); } if (ruleExists(identity, action)) { return; } - // expand actions - possibly multiply number by - if (isSet(EXPAND)) - { - if (action.getOperation() == Operation.CREATE && action.getObjectType() == ObjectType.TOPIC) - { - addRule(null, identity, noLog(permission), new Action(Operation.BIND, ObjectType.EXCHANGE, - new ObjectProperties("amq.topic", action.getProperties().get(ObjectProperties.Property.NAME)))); - ObjectProperties topicProperties = new ObjectProperties(); - topicProperties.put(ObjectProperties.Property.DURABLE, true); - addRule(null, identity, permission, new Action(Operation.CREATE, ObjectType.QUEUE, topicProperties)); - return; - } - if (action.getOperation() == Operation.DELETE && action.getObjectType() == ObjectType.TOPIC) - { - addRule(null, identity, noLog(permission), new Action(Operation.UNBIND, ObjectType.EXCHANGE, - new ObjectProperties("amq.topic", action.getProperties().get(ObjectProperties.Property.NAME)))); - ObjectProperties topicProperties = new ObjectProperties(); - topicProperties.put(ObjectProperties.Property.DURABLE, true); - addRule(null, identity, permission, new Action(Operation.DELETE, ObjectType.QUEUE, topicProperties)); - return; - } - } - - // transitive action dependencies - if (isSet(TRANSITIVE)) - { - if (action.getOperation() == Operation.CREATE && action.getObjectType() == ObjectType.QUEUE) - { - ObjectProperties exchProperties = new ObjectProperties(action.getProperties()); - exchProperties.setName(ExchangeDefaults.DEFAULT_EXCHANGE_NAME); - exchProperties.put(ObjectProperties.Property.ROUTING_KEY, action.getProperties().get(ObjectProperties.Property.NAME)); - addRule(null, identity, noLog(permission), new Action(Operation.BIND, ObjectType.EXCHANGE, exchProperties)); - if (action.getProperties().isSet(ObjectProperties.Property.AUTO_DELETE)) - { - addRule(null, identity, noLog(permission), new Action(Operation.DELETE, ObjectType.QUEUE, action.getProperties())); - } - } - else if (action.getOperation() == Operation.DELETE && action.getObjectType() == ObjectType.QUEUE) - { - ObjectProperties exchProperties = new ObjectProperties(action.getProperties()); - exchProperties.setName(ExchangeDefaults.DEFAULT_EXCHANGE_NAME); - exchProperties.put(ObjectProperties.Property.ROUTING_KEY, action.getProperties().get(ObjectProperties.Property.NAME)); - addRule(null, identity, noLog(permission), new Action(Operation.UNBIND, ObjectType.EXCHANGE, exchProperties)); - } - else if (action.getOperation() != Operation.ACCESS && action.getObjectType() != ObjectType.VIRTUALHOST) - { - addRule(null, identity, noLog(permission), new Action(Operation.ACCESS, ObjectType.VIRTUALHOST)); - } - } - // set rule number if needed Rule rule = new Rule(number, identity, action, permission); if (rule.getNumber() == null) @@ -392,24 +332,29 @@ public class RuleSet // Create the action to check Action action = new Action(operation, objectType, properties); + if(_logger.isDebugEnabled()) + { + _logger.debug("Checking action: " + action); + } + // get the list of rules relevant for this request List<Rule> rules = getRules(subject, operation, objectType); if (rules == null) { - if (isSet(CONTROLLED)) + if(_logger.isDebugEnabled()) { - // Abstain if there are no rules for this operation - return Result.ABSTAIN; - } - else - { - return getDefault(); + _logger.debug("No rules found, returning default result"); } + return getDefault(); } // Iterate through a filtered set of rules dealing with this identity and operation for (Rule current : rules) { + if(_logger.isDebugEnabled()) + { + _logger.debug("Checking against rule: " + current); + } // Check if action matches if (action.matches(current.getAction())) { @@ -480,6 +425,15 @@ public class RuleSet _config.put(key, value); } + /** + * Returns all rules in the {@link RuleSet}. Primarily intended to support unit-testing. + * @return map of rules + */ + public Map<Integer, Rule> getAllRules() + { + return Collections.unmodifiableMap(_rules); + } + private boolean isRelevant(final Set<Principal> principals, final Rule rule) { if (rule.getIdentity().equalsIgnoreCase(Rule.ALL)) diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java index a7b3059262..a97b66a287 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java @@ -101,6 +101,7 @@ public class AccessControl extends AbstractPlugin return Result.ABSTAIN; } + _logger.debug("Checking " + operation + " " + objectType); return _ruleSet.check(subject, operation, objectType, properties); } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java index f7db740ebc..b5c89910a6 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java @@ -45,7 +45,7 @@ public class AccessControlConfiguration extends ConfigurationPlugin public List<String> getParentPaths() { - return Arrays.asList("security.aclv2", "virtualhosts.virtualhost.security.aclv2"); + return Arrays.asList("security.acl", "virtualhosts.virtualhost.security.acl"); } }; diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java index e16f9943ba..aa3982df71 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java @@ -22,12 +22,19 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.PrintWriter; +import java.util.Map; import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectProperties.Property; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.config.ConfigurationFile; import org.apache.qpid.server.security.access.config.PlainConfiguration; +import org.apache.qpid.server.security.access.config.Rule; +import org.apache.qpid.server.security.access.config.RuleSet; /** * These tests check that the ACL file parsing works correctly. @@ -37,7 +44,7 @@ import org.apache.qpid.server.security.access.config.PlainConfiguration; */ public class PlainConfigurationTest extends TestCase { - public void writeACLConfig(String...aclData) throws Exception + private PlainConfiguration writeACLConfig(String...aclData) throws Exception { File acl = File.createTempFile(getClass().getName() + getName(), "acl"); acl.deleteOnExit(); @@ -51,8 +58,9 @@ public class PlainConfigurationTest extends TestCase aclWriter.close(); // Load ruleset - ConfigurationFile configFile = new PlainConfiguration(acl); + PlainConfiguration configFile = new PlainConfiguration(acl); configFile.load(); + return configFile; } public void testMissingACLConfig() throws Exception @@ -191,4 +199,197 @@ public class PlainConfigurationTest extends TestCase assertEquals(String.format(PlainConfiguration.PROPERTY_NO_VALUE_MSG, 1), ce.getMessage()); } } + + /** + * Tests interpretation of an acl rule with no object properties. + * + */ + public void testValidRule() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL DENY-LOG user1 ACCESS VIRTUALHOST"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl rule with object properties quoted in single quotes. + */ + public void testValidRuleWithSingleQuotedProperty() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = \'value\'"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.CREATE, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); + final ObjectProperties expectedProperties = new ObjectProperties(); + expectedProperties.setName("value"); + assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl rule with object properties quoted in double quotes. + */ + public void testValidRuleWithDoubleQuotedProperty() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE name = \"value\""); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "all", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.CREATE, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); + final ObjectProperties expectedProperties = new ObjectProperties(); + expectedProperties.setName("value"); + assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl rule with many object properties. + */ + public void testValidRuleWithManyProperties() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL ALLOW admin DELETE QUEUE name=name1 owner = owner1"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "admin", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.DELETE, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.QUEUE, rule.getAction().getObjectType()); + final ObjectProperties expectedProperties = new ObjectProperties(); + expectedProperties.setName("name1"); + expectedProperties.put(Property.OWNER, "owner1"); + assertEquals("Rule has unexpected operation", expectedProperties, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl rule with object properties containing wildcards. Values containing + * hashes must be quoted otherwise they are interpreted as comments. + */ + public void testValidRuleWithWildcardProperties() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL ALLOW all CREATE EXCHANGE routingKey = \'news.#\'", + "ACL ALLOW all CREATE EXCHANGE routingKey = \'news.co.#\'", + "ACL ALLOW all CREATE EXCHANGE routingKey = *.co.medellin"); + final RuleSet rs = config.getConfiguration(); + assertEquals(3, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(3, rules.size()); + final Rule rule1 = rules.get(0); + assertEquals("Rule has unexpected identity", "all", rule1.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.CREATE, rule1.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule1.getAction().getObjectType()); + final ObjectProperties expectedProperties1 = new ObjectProperties(); + expectedProperties1.put(Property.ROUTING_KEY,"news.#"); + assertEquals("Rule has unexpected object properties", expectedProperties1, rule1.getAction().getProperties()); + + final Rule rule2 = rules.get(10); + final ObjectProperties expectedProperties2 = new ObjectProperties(); + expectedProperties2.put(Property.ROUTING_KEY,"news.co.#"); + assertEquals("Rule has unexpected object properties", expectedProperties2, rule2.getAction().getProperties()); + + final Rule rule3 = rules.get(20); + final ObjectProperties expectedProperties3 = new ObjectProperties(); + expectedProperties3.put(Property.ROUTING_KEY,"*.co.medellin"); + assertEquals("Rule has unexpected object properties", expectedProperties3, rule3.getAction().getProperties()); + } + + /** + * Tests that rules are case insignificant. + */ + public void testMixedCaseRuleInterpretation() throws Exception + { + final PlainConfiguration config = writeACLConfig("AcL deny-LOG user1 BiND Exchange name=AmQ.dIrect"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.BIND, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.EXCHANGE, rule.getAction().getObjectType()); + final ObjectProperties expectedProperties = new ObjectProperties("amq.direct"); + assertEquals("Rule has unexpected object properties", expectedProperties, rule.getAction().getProperties()); + } + + /** + * Tests whitespace is supported. Note that currently the Java implementation permits comments to + * be introduced anywhere in the ACL, whereas the C++ supports only whitespace at the beginning of + * of line. + */ + public void testCommentsSuppported() throws Exception + { + final PlainConfiguration config = writeACLConfig("#Comment", + "ACL DENY-LOG user1 ACCESS VIRTUALHOST # another comment", + " # final comment with leading whitespace"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl rule using mixtures of tabs/spaces as token separators. + * + */ + public void testWhitespace() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL\tDENY-LOG\t\t user1\t \tACCESS VIRTUALHOST"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); + } + + /** + * Tests interpretation of an acl utilising line continuation. + */ + public void testLineContination() throws Exception + { + final PlainConfiguration config = writeACLConfig("ACL DENY-LOG user1 \\", + "ACCESS VIRTUALHOST"); + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map<Integer, Rule> rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", "user1", rule.getIdentity()); + assertEquals("Rule has unexpected operation", Operation.ACCESS, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", ObjectType.VIRTUALHOST, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); + } + } diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java index bd9deac153..4d46a32f45 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java @@ -69,7 +69,6 @@ public class RuleSetTest extends QpidTestCase super.setUp(); _ruleSet = new RuleSet(); - _ruleSet.configure(RuleSet.TRANSITIVE, Boolean.FALSE); } @Override diff --git a/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF b/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF index 49e90c6aad..0bd0a835e4 100644 --- a/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF +++ b/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF @@ -9,7 +9,8 @@ Bundle-Version: 1.0.0 Bundle-Activator: org.apache.qpid.shutdown.Activator Import-Package: javax.management;resolution:=optional, org.apache.log4j, - org.osgi.framework + org.osgi.framework, + org.apache.qpid.server.management Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy diff --git a/qpid/java/broker-plugins/experimental/shutdown/build.xml b/qpid/java/broker-plugins/experimental/shutdown/build.xml index ec4fce374e..cb4254806b 100644 --- a/qpid/java/broker-plugins/experimental/shutdown/build.xml +++ b/qpid/java/broker-plugins/experimental/shutdown/build.xml @@ -20,7 +20,7 @@ --> <project name="AMQ Broker Shutdown Plugin" default="build"> - <property name="module.depends" value="common broker broker-plugins"/> + <property name="module.depends" value="common broker management/common broker-plugins"/> <property name="module.test.depends" value="test broker/test management/common client systests"/> <property name="module.manifest" value="MANIFEST.MF"/> <property name="module.plugin" value="true"/> diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java index ad5e7707b6..2b7fa33784 100644 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java +++ b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java @@ -19,11 +19,6 @@ */ package org.apache.qpid.shutdown; -import java.lang.management.ManagementFactory; - -import javax.management.InstanceNotFoundException; -import javax.management.MBeanServer; -import javax.management.ObjectName; import org.apache.log4j.Logger; import org.osgi.framework.BundleActivator; @@ -33,20 +28,17 @@ public class Activator implements BundleActivator { private static final Logger _logger = Logger.getLogger(Activator.class); - private static final String SHUTDOWN_MBEAN_NAME = "org.apache.qpid:type=ShutdownMBean"; + private Shutdown _shutdown = null; /** @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ public void start(BundleContext ctx) throws Exception { - Shutdown shutdown = new Shutdown(); + _shutdown = new Shutdown(); if (ctx != null) { - ctx.registerService(ShutdownMBean.class.getName(), shutdown, null); + ctx.registerService(ShutdownMBean.class.getName(), _shutdown, null); } - // MBean registration - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - ObjectName name = new ObjectName(SHUTDOWN_MBEAN_NAME); - mbs.registerMBean(shutdown, name); + _shutdown.register(); _logger.info("Shutdown plugin MBean registered"); } @@ -54,16 +46,10 @@ public class Activator implements BundleActivator /** @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext ctx) throws Exception { - // Unregister MBean - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - ObjectName name = new ObjectName(SHUTDOWN_MBEAN_NAME); - try - { - mbs.unregisterMBean(name); - } - catch (InstanceNotFoundException e) + if (_shutdown != null) { - //ignore + _shutdown.unregister(); + _shutdown = null; } _logger.info("Shutdown plugin MBean unregistered"); diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java index 9a6f85fe9c..cef4a42b64 100644 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java +++ b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java @@ -27,21 +27,30 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import javax.management.NotCompliantMBeanException; + import org.apache.log4j.Logger; +import org.apache.qpid.server.management.DefaultManagedObject; /** * Implementation of the JMX broker shutdown plugin. */ -public class Shutdown implements ShutdownMBean +public class Shutdown extends DefaultManagedObject implements ShutdownMBean { + private static final Logger _logger = Logger.getLogger(Shutdown.class); - private static final String FORMAT = "yyyyy/MM/dd hh:mm:ss"; + private static final String FORMAT = "yyyy/MM/dd HH:mm:ss"; private static final int THREAD_COUNT = 1; private static final ScheduledExecutorService EXECUTOR = new ScheduledThreadPoolExecutor(THREAD_COUNT); private final Runnable _shutdown = new SystemExiter(); + public Shutdown() throws NotCompliantMBeanException + { + super(ShutdownMBean.class, ShutdownMBean.TYPE); + } + /** @see ShutdownMBean#shutdown() */ public void shutdown() { @@ -50,14 +59,22 @@ public class Shutdown implements ShutdownMBean } /** @see ShutdownMBean#shutdown(long) */ - public void shutdown(long delay) + public void shutdown(final long delay) { - _logger.info("Scheduled broker shutdown after " + delay + "ms"); - shutdownBroker(delay); + if (delay < 0) + { + _logger.info("Shutting down at user's request"); + shutdownBroker(0); + } + else + { + _logger.info("Scheduled broker shutdown after " + delay + "ms"); + shutdownBroker(delay); + } } /** @see ShutdownMBean#shutdownAt(String) */ - public void shutdownAt(String when) + public void shutdownAt(final String when) { Date date; DateFormat df = new SimpleDateFormat(FORMAT); @@ -101,4 +118,13 @@ public class Shutdown implements ShutdownMBean System.exit(0); } } + + /** + * @see org.apache.qpid.server.management.ManagedObject#getObjectInstanceName() + */ + @Override + public String getObjectInstanceName() + { + return "Shutdown"; + } } diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java index 6294f869e9..48d2eab8df 100644 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java +++ b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java @@ -19,6 +19,9 @@ */ package org.apache.qpid.shutdown; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter; + /** * Shutdown plugin JMX MBean interface. * @@ -26,9 +29,12 @@ package org.apache.qpid.shutdown; */ public interface ShutdownMBean { + static final String TYPE = "Shutdown"; + /** * Broker will be shut down immediately. */ + @MBeanOperation(name="shutdown", description="Shut down immediately") public void shutdown(); /** @@ -36,12 +42,14 @@ public interface ShutdownMBean * * @param delay the number of ms to wait */ - public void shutdown(long delay); + @MBeanOperation(name="shutdown", description="Shutdown after the specified delay (ms)") + public void shutdown(@MBeanOperationParameter(name="when", description="delay (ms)")long delay); /** * Broker will be shutdown at the specified date and time. * * @param when the date and time to shutdown */ - public void shutdownAt(String when); + @MBeanOperation(name="shutdownAt", description="Shutdown at the specified date and time (yyyy/MM/dd HH:mm:ss)") + public void shutdownAt(@MBeanOperationParameter(name="when", description="shutdown date/time (yyyy/MM/dd HH:mm:ss)")String when); } diff --git a/qpid/java/broker/bin/msTool.sh b/qpid/java/broker/bin/msTool.sh deleted file mode 100755 index e190a0a46a..0000000000 --- a/qpid/java/broker/bin/msTool.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# -# 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. -# - -die() { - if [[ $1 = -usage ]]; then - shift - usage=true - else - usage=false - fi - echo "$@" - $usage && echo - $usage && usage - exit 1 -} - -cygwin=false -if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then - cygwin=true -fi - -if [ -z "$QPID_TOOLS" ]; then - if [ -z "$QPID_HOME" ]; then - die "QPID_TOOLS must be set" - else - QPID_TOOLS=$QPID_HOME - fi -fi - -if $cygwin; then - QPID_TOOLS=$(cygpath -w $QPID_TOOLS) -fi - -# Set classpath to include Qpid jar with all required jars in manifest -QPID_LIBS=$QPID_TOOLS/lib/qpid-all.jar - -# Set other variables used by the qpid-run script before calling -export JAVA=java \ - JAVA_VM=-server \ - JAVA_OPTS=-Dlog4j.configuration=file:$QPID_TOOLS/etc/mstool-log4j.xml \ - QPID_CLASSPATH=$QPID_LIBS - -. qpid-run org.apache.qpid.tools.messagestore.MessageStoreTool "$@" diff --git a/qpid/java/broker/build.xml b/qpid/java/broker/build.xml index 6ea2b9a63e..4a42e5cdb8 100644 --- a/qpid/java/broker/build.xml +++ b/qpid/java/broker/build.xml @@ -72,14 +72,26 @@ <fixcrlf srcdir="${module.release}/bin" fixlast="true" eol="dos" includes="*.bat"/> </target> - <target name="release-bin-other" description="copy broker-plugins into module release"> + <target name="release-bin-other" depends="release-bin-other-bdbstore" description="copy broker-plugins into module release"> <copy todir="${module.release}/lib/plugins" failonerror="true"> <fileset dir="${build.lib}/plugins"/> </copy> - <!--copy optional bdbstore module if it exists --> - <copy todir="${module.release}/lib/" failonerror="false"> + </target> + + <target name="release-bin-other-bdbstore" depends="check-bdbstore-requested" if="bdbstore-requested" + description="copy bdbstore items into module release"> + <copy todir="${module.release}/lib/" failonerror="true"> <fileset file="${build.lib}/${project.name}-bdbstore-${project.version}.jar"/> </copy> + <copy todir="${module.release}/bin" failonerror="true" flatten="true"> + <fileset dir="${basedir}/../bdbstore/bin"/> + </copy> + </target> + + <target name="check-bdbstore-requested"> + <condition property="bdbstore-requested"> + <contains string="${modules.opt}" substring="bdbstore"/> + </condition> </target> <target name="release-bin" depends="release-bin-tasks"/> diff --git a/qpid/java/broker/etc/broker_example.acl b/qpid/java/broker/etc/broker_example.acl new file mode 100644 index 0000000000..93955bb7f9 --- /dev/null +++ b/qpid/java/broker/etc/broker_example.acl @@ -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. +# + +### EXAMPLE ACL V2 FILE + +### DEFINE GROUPS ### + +#Define a 'messaging-users' group with users 'client' and 'server' in it +GROUP messaging-users client server + +### MANAGEMENT #### + +#Allow 'guest' to perform read operations on the Serverinformation mbean and view logger levels +ACL ALLOW-LOG guest ACCESS METHOD component="ServerInformation" +ACL ALLOW-LOG guest ACCESS METHOD component="LoggingManagement" name="viewEffectiveRuntimeLoggerLevels" + +#Allow 'admin' all management operations +ACL ALLOW-LOG admin ALL METHOD + +### MESSAGING ### + +#Example permissions for request-response based messaging. + +#Allow 'messaging-users' group to connect to the virtualhost +ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST + +# Client side +# Allow the 'client' user to publish requests to the request queue and create, consume from, and delete temporary reply queues. +ACL ALLOW-LOG client CREATE QUEUE temporary="true" +ACL ALLOW-LOG client CONSUME QUEUE temporary="true" +ACL ALLOW-LOG client DELETE QUEUE temporary="true" +ACL ALLOW-LOG client BIND EXCHANGE name="amq.direct" temporary="true" +ACL ALLOW-LOG client UNBIND EXCHANGE name="amq.direct" temporary="true" +ACL ALLOW-LOG client PUBLISH EXCHANGE name="amq.direct" routingKey="example.RequestQueue" + +# Server side +# Allow the 'server' user to create and consume from the request queue and publish a response to the temporary response queue created by +# client. +ACL ALLOW-LOG server CREATE QUEUE name="example.RequestQueue" +ACL ALLOW-LOG server CONSUME QUEUE name="example.RequestQueue" +ACL ALLOW-LOG server BIND EXCHANGE +ACL ALLOW-LOG server PUBLISH EXCHANGE name="amq.direct" routingKey="TempQueue*" + +### DEFAULT ### + +#Deny all users from performing all operations +ACL DENY-LOG all all diff --git a/qpid/java/broker/etc/config.xml b/qpid/java/broker/etc/config.xml index d18e1392e6..25fda69f68 100644 --- a/qpid/java/broker/etc/config.xml +++ b/qpid/java/broker/etc/config.xml @@ -80,7 +80,12 @@ </principal-database> </pd-auth-manager> - <allow-all /> + <!-- By default, all authenticated users have permissions to perform all actions --> + + <!-- ACL Example + This example illustrates securing the both Management (JMX) and Messaging. + <acl>${conf}/broker_example.acl</acl> + --> <msg-auth>false</msg-auth> </security> diff --git a/qpid/java/broker/etc/debug.log4j.xml b/qpid/java/broker/etc/debug.log4j.xml deleted file mode 100644 index fc0bd9f34f..0000000000 --- a/qpid/java/broker/etc/debug.log4j.xml +++ /dev/null @@ -1,114 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - - - 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. - - - --><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> - -<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="null" threshold="null"> - <appender class="org.apache.log4j.QpidCompositeRollingAppender" name="ArchivingFileAppender"> - <!-- Ensure that logs allways have the dateFormat set--> - <param name="StaticLogFileName" value="false"/> - <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/> - <param name="Append" value="false"/> - <!-- Change the direction so newer files have bigger numbers --> - <!-- So log.1 is written then log.2 etc This prevents a lot of file renames at log rollover --> - <param name="CountDirection" value="1"/> - <!-- Use default 10MB --> - <!--param name="MaxFileSize" value="100000"/--> - <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm"/> - <!-- Unlimited number of backups --> - <param name="MaxSizeRollBackups" value="-1"/> - <!-- Compress(gzip) the backup files--> - <param name="CompressBackupFiles" value="true"/> - <!-- Compress the backup files using a second thread --> - <param name="CompressAsync" value="true"/> - <!-- Start at zero numbered files--> - <param name="ZeroBased" value="true"/> - <!-- Backup Location --> - <param name="backupFilesToPath" value="${QPID_WORK}/backup/log"/> - - <layout class="org.apache.log4j.PatternLayout"> - <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> - </layout> - </appender> - - <appender class="org.apache.log4j.FileAppender" name="FileAppender"> - <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/> - <param name="Append" value="false"/> - - <layout class="org.apache.log4j.PatternLayout"> - <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> - </layout> - </appender> - - <appender class="org.apache.log4j.FileAppender" name="AlertFile"> - <param name="File" value="${QPID_WORK}/log/alert.log"/> - <param name="Append" value="false"/> - - <layout class="org.apache.log4j.PatternLayout"> - <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> - </layout> - </appender> - - <appender class="org.apache.log4j.ConsoleAppender" name="STDOUT"> - - <layout class="org.apache.log4j.PatternLayout"> - <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> - </layout> - </appender> - - <category additivity="true" name="Qpid.Broker"> - <priority value="debug"/> - <appender-ref ref="AlertFile"/> - <!--appender-ref ref="STDOUT"/--> - </category> - - - <category additivity="true" name="org.apache.qpid.server.queue.AMQQueueMBean"> - <priority value="info"/> - <appender-ref ref="AlertFile"/> - </category> - - - <!-- Provide warnings to standard output --> - <!--category additivity="true" name="org.apache.qpid"> - <priority value="warn"/> - <appender-ref ref="STDOUT"/> - </category--> - - - <!-- Additional level settings for debugging --> - <!-- Each class in the Broker is a category that can have its logging level adjusted. --> - <!-- This will provide more details if available about that classes processing. --> - <!--category additivity="true" name="org.apache.qpid.server.txn"> - <priority value="debug"/> - </category>--> - - <!--<category additivity="true" name="org.apache.qpid.server.store"> - <priority value="debug"/> - </category--> - - <!-- Log all info events to file --> - <root> - <priority value="debug"/> - <appender-ref ref="STDOUT"/> - <!--appender-ref ref="FileAppender"/--> - </root> - -</log4j:configuration> diff --git a/qpid/java/broker/etc/md5passwd b/qpid/java/broker/etc/md5passwd index 6a149919de..59354a21f5 100644 --- a/qpid/java/broker/etc/md5passwd +++ b/qpid/java/broker/etc/md5passwd @@ -17,5 +17,6 @@ # under the License.
#
guest:CE4DQ6BIb/BVMN9scFyLtA==
+client:CE4DQ6BIb/BVMN9scFyLtA==
+server:CE4DQ6BIb/BVMN9scFyLtA==
admin:ISMvKXpXpadDiUoOSoAfww==
-user:aBzonUodYLhwSa8s9A10sA==
diff --git a/qpid/java/broker/etc/mstool-log4j.xml b/qpid/java/broker/etc/mstool-log4j.xml deleted file mode 100644 index 8c46010e2d..0000000000 --- a/qpid/java/broker/etc/mstool-log4j.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0"?> -<!-- - - - - 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. - - - --> -<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> -<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> - - <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> - - <layout class="org.apache.log4j.PatternLayout"> - <!--param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/--> - <param name="ConversionPattern" value="%d %-5p [%t] (%F:%L) - %m%n"/> - </layout> - </appender> - - <category name="org.apache.qpid.tools"> - <priority value="info"/> - </category> - - <category name="org.apache.qpid"> - <priority value="error"/> - </category> - - <category name="org.apache.qpid.server.security"> - <priority value="error"/> - </category> - - <category name="org.apache.qpid.server.management"> - <priority value="error"/> - </category> - - - <root> - <priority value="info"/> - <appender-ref ref="STDOUT"/> - </root> -</log4j:configuration> diff --git a/qpid/java/broker/etc/qpid-server.conf b/qpid/java/broker/etc/qpid-server.conf deleted file mode 100644 index 8a16849b04..0000000000 --- a/qpid/java/broker/etc/qpid-server.conf +++ /dev/null @@ -1,25 +0,0 @@ -# -# 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. -# - -QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar - -export JAVA=java \ - JAVA_VM=-server \ - JAVA_MEM=-Xmx1024m \ - CLASSPATH=$QPID_LIBS diff --git a/qpid/java/broker/etc/qpid-server.conf.jpp b/qpid/java/broker/etc/qpid-server.conf.jpp deleted file mode 100644 index 0378c82fd9..0000000000 --- a/qpid/java/broker/etc/qpid-server.conf.jpp +++ /dev/null @@ -1,48 +0,0 @@ -# -# 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. -# - -QPID_LIBS=$(build-classpath commons-beanutils \ - commons-beanutils-core \ - commons-cli \ - commons-codec \ - commons-collections \ - commons-configuration \ - commons-digester \ - commons-lang \ - commons-logging \ - commons-logging-api \ - dom4j \ - geronimo-jms-1.1-api \ - isorelax \ - jaxen \ - log4j \ - mina/core \ - mina/filter-ssl \ - mina/java5 \ - msv-msv \ - qpid-broker \ - qpid-client \ - qpid-common \ - relaxngDatatype \ - slf4j) - -export JAVA=java \ - JAVA_VM=-server \ - JAVA_MEM=-Xmx1024m \ - CLASSPATH=$QPID_LIBS diff --git a/qpid/java/broker/etc/qpid.passwd b/qpid/java/broker/etc/qpid.passwd deleted file mode 100644 index dbfb9d1923..0000000000 --- a/qpid/java/broker/etc/qpid.passwd +++ /dev/null @@ -1,23 +0,0 @@ -#
-# 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.
-#
-guest:CE4DQ6BIb/BVMN9scFyLtA==
-admin:ISMvKXpXpadDiUoOSoAfww==
-user:CE4DQ6BIb/BVMN9scFyLtA==
-server:CE4DQ6BIb/BVMN9scFyLtA==
-client:CE4DQ6BIb/BVMN9scFyLtA==
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index d1ea5dba69..01a0d9900d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -20,8 +20,8 @@ package org.apache.qpid.server; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Map; import javax.management.JMException; import javax.management.MBeanException; @@ -30,6 +30,7 @@ import javax.management.ObjectName; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.management.common.mbeans.ManagedBroker; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; @@ -60,6 +61,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr private final QueueRegistry _queueRegistry; private final ExchangeRegistry _exchangeRegistry; private final ExchangeFactory _exchangeFactory; + private final Exchange _defaultExchange; private final DurableConfigurationStore _durableConfig; private final VirtualHostImpl.VirtualHostMBean _virtualHostMBean; @@ -74,6 +76,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr _queueRegistry = virtualHost.getQueueRegistry(); _exchangeRegistry = virtualHost.getExchangeRegistry(); + _defaultExchange = _exchangeRegistry.getDefaultExchange(); _durableConfig = virtualHost.getDurableConfigurationStore(); _exchangeFactory = virtualHost.getExchangeFactory(); } @@ -241,7 +244,13 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr */ public void createNewQueue(String queueName, String owner, boolean durable) throws JMException, MBeanException { - AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); + createNewQueue(queueName, owner, durable, null); + } + + public void createNewQueue(String queueName, String owner, boolean durable, Map<String,Object> arguments) throws JMException + { + final AMQShortString queueNameAsAMQShortString = new AMQShortString(queueName); + AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); if (queue != null) { throw new JMException("The queue \"" + queueName + "\" already exists."); @@ -256,13 +265,21 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr ownerShortString = new AMQShortString(owner); } - queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, false, getVirtualHost(), null); + FieldTable args = null; + if(arguments != null) + { + args = FieldTable.convertToFieldTable(arguments); + } + final VirtualHost virtualHost = getVirtualHost(); + + queue = AMQQueueFactory.createAMQQueueImpl(queueNameAsAMQShortString, durable, ownerShortString, + false, false, getVirtualHost(), args); if (queue.isDurable() && !queue.isAutoDelete()) { - _durableConfig.createQueue(queue); + _durableConfig.createQueue(queue, args); } - _queueRegistry.registerQueue(queue); + virtualHost.getBindingFactory().addBinding(queueName, queue, _defaultExchange, null); } catch (AMQException ex) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 2a96426f02..30d620401f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -22,7 +22,6 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; -import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQMethodBody; @@ -53,6 +52,7 @@ import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; @@ -63,6 +63,7 @@ import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.InboundMessageAdapter; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; @@ -693,6 +694,31 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } + public boolean isMaxDeliveryCountEnabled(final long deliveryTag) + { + final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + if (queueEntry != null) + { + final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + return maximumDeliveryCount > 0; + } + + return false; + } + + public boolean isDeliveredTooManyTimes(final long deliveryTag) + { + final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + if (queueEntry != null) + { + final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + final int numDeliveries = queueEntry.getDeliveryCount(); + return maximumDeliveryCount != 0 && numDeliveries >= maximumDeliveryCount; + } + + return false; + } + /** * Called to resend all outstanding unacknowledged messages to this same channel. * @@ -740,9 +766,9 @@ public class AMQChannel implements SessionConfig, AMQSessionModel QueueEntry message = entry.getValue(); long deliveryTag = entry.getKey(); + //Amend the delivery counter as the client hasn't seen these messages yet. + message.decrementDeliveryCount(); - - ServerMessage msg = message.getMessage(); AMQQueue queue = message.getQueue(); // Our Java Client will always suspend the channel when resending! @@ -800,6 +826,10 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { QueueEntry message = entry.getValue(); long deliveryTag = entry.getKey(); + + //Amend the delivery counter as the client hasn't seen these messages yet. + message.decrementDeliveryCount(); + _unacknowledgedMessageMap.remove(deliveryTag); message.setRedelivered(); @@ -1060,6 +1090,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel getProtocolSession().getProtocolOutputConverter().writeDeliver(entry, getChannelId(), deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); + entry.incrementDeliveryCount(); } }; @@ -1248,7 +1279,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { private final Collection<QueueEntry> _ackedMessages; - public MessageAcknowledgeAction(Collection<QueueEntry> ackedMessages) { _ackedMessages = ackedMessages; @@ -1481,4 +1511,54 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } } } + + public void deadLetter(long deliveryTag) throws AMQException + { + final UnacknowledgedMessageMap unackedMap = getUnacknowledgedMessageMap(); + final QueueEntry rejectedQueueEntry = unackedMap.get(deliveryTag); + + if (rejectedQueueEntry == null) + { + _logger.warn("No message found, unable to DLQ delivery tag: " + deliveryTag); + return; + } + else + { + final ServerMessage msg = rejectedQueueEntry.getMessage(); + + final AMQQueue queue = rejectedQueueEntry.getQueue(); + + final Exchange altExchange = queue.getAlternateExchange(); + unackedMap.remove(deliveryTag); + + if (altExchange == null) + { + _logger.debug("No alternate exchange configured for queue, must discard the message as unable to DLQ: delivery tag: " + deliveryTag); + _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), queue.getName(), msg.getRoutingKey())); + rejectedQueueEntry.discard(); + return; + } + + final InboundMessage m = new InboundMessageAdapter(rejectedQueueEntry); + + final ArrayList<? extends BaseQueue> destinationQueues = altExchange.route(m); + + if (destinationQueues == null || destinationQueues.isEmpty()) + { + _logger.debug("Routing process provided no queues to enqueue the message on, must discard message as unable to DLQ: delivery tag: " + deliveryTag); + _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), altExchange.getName())); + rejectedQueueEntry.discard(); + return; + } + + rejectedQueueEntry.routeToAlternate(); + + //output operational logging for each delivery post commit + for (final BaseQueue destinationQueue : destinationQueues) + { + _actor.message(_logSubject, ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), destinationQueue.getNameShortString().asString())); + } + + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java index 400ce50bc4..94ab43c851 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java @@ -170,11 +170,17 @@ public class BindingFactory { arguments = Collections.emptyMap(); } - - //Perform ACLs - if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + + // The default exchange bindings must reflect the existence of queues, allow + // all operations on it to succeed. It is up to the broker to prevent illegal + // attempts at binding to this exchange, not the ACLs. + if(exchange != _defaultExchange) { - throw new AMQSecurityException("Permission denied: binding " + bindingKey); + //Perform ACLs + if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + { + throw new AMQSecurityException("Permission denied: binding " + bindingKey); + } } BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); @@ -238,10 +244,16 @@ public class BindingFactory arguments = Collections.emptyMap(); } - // Check access - if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + // The default exchange bindings must reflect the existence of queues, allow + // all operations on it to succeed. It is up to the broker to prevent illegal + // attempts at binding to this exchange, not the ACLs. + if(exchange != _defaultExchange) { - throw new AMQSecurityException("Permission denied: binding " + bindingKey); + // Check access + if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + { + throw new AMQSecurityException("Permission denied: unbinding " + bindingKey); + } } BindingImpl b = _bindings.remove(new BindingImpl(bindingKey,queue,exchange,arguments)); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java index 3a28b65a08..759907d4bd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java @@ -62,7 +62,10 @@ public class QueueConfiguration extends ConfigurationPlugin "capacity", "flowResumeCapacity", "lvq", - "lvqKey" + "lvqKey", + "sortKey", + "maximumDeliveryCount", + "deadLetterQueues" }; } @@ -167,11 +170,30 @@ public class QueueConfiguration extends ConfigurationPlugin return getStringValue("lvqKey", null); } + public boolean isTopic() { return getBooleanValue("topic"); } + public String getQueueSortKey() + { + return getStringValue("sortKey", null); + } + + public int getMaxDeliveryCount() + { + return getIntValue("maximumDeliveryCount", _vHostConfig.getMaxDeliveryCount()); + } + + /** + * Check if dead letter queue delivery is enabled, deferring to the virtualhost configuration if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getBooleanValue("deadLetterQueues", _vHostConfig.isDeadLetterQueueEnabled()); + } + public static class QueueConfig extends ConfigurationPlugin { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 0d347873c2..4b42e39aa1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -40,6 +40,8 @@ import org.apache.commons.configuration.SystemConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.exchange.DefaultExchangeFactory; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.signal.SignalHandlerTask; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -805,4 +807,39 @@ public class ServerConfiguration extends ConfigurationPlugin final List<String> disabledFeatures = getListValue("disabledFeatures", Collections.emptyList()); return disabledFeatures; } + + public boolean getManagementRightsInferAllAccess() + { + return getBooleanValue("management.managementRightsInferAllAccess", true); + } + + public int getMaxDeliveryCount() + { + return getConfig().getInt("maximumDeliveryCount", 0); + } + + /** + * Check if dead letter queue delivery is enabled, defaults to disabled if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getConfig().getBoolean("deadLetterQueues", false); + } + + /** + * String to affix to end of queue name when generating an alternate exchange for DLQ purposes. + */ + public String getDeadLetterExchangeSuffix() + { + return getConfig().getString("deadLetterExchangeSuffix", DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + } + + /** + * String to affix to end of queue name when generating a queue for DLQ purposes. + */ + public String getDeadLetterQueueSuffix() + { + return getConfig().getString("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index 6729a5ce0f..c4e4f701a8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -347,4 +347,18 @@ public class VirtualHostConfiguration extends ConfigurationPlugin { return getLongValue("transactionTimeout.idleClose", 0L); } + + public int getMaxDeliveryCount() + { + return getIntValue("queues.maximumDeliveryCount", ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount()); + } + + /** + * Check if dead letter queue delivery is enabled, deferring to the broker configuration if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getBooleanValue("queues.deadLetterQueues", ApplicationRegistry.getInstance().getConfiguration().isDeadLetterQueueEnabled()); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java index 0638ea362f..6f8020fc54 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java @@ -87,6 +87,19 @@ public class SlowConsumerDetectionQueueConfiguration extends ConfigurationPlugin @Override public void validateConfiguration() throws ConfigurationException { + PluginManager pluginManager; + try + { + pluginManager = ApplicationRegistry.getInstance().getPluginManager(); + } + catch (IllegalStateException ise) + { + // We see this happen during shutdown due to asynchronous reconfig performed IO threads + // running at the same time as the shutdown handler. + _policyPlugin = null; + return; + } + if (!containsPositiveLong("messageAge") && !containsPositiveLong("depth") && !containsPositiveLong("messageCount")) @@ -96,8 +109,6 @@ public class SlowConsumerDetectionQueueConfiguration extends ConfigurationPlugin } SlowConsumerDetectionPolicyConfiguration policyConfig = getConfiguration(SlowConsumerDetectionPolicyConfiguration.class.getName()); - - PluginManager pluginManager = ApplicationRegistry.getInstance().getPluginManager(); Map<String, SlowConsumerPolicyPluginFactory> factories = pluginManager.getSlowConsumerPlugins(); if (policyConfig == null) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 7837a9bc38..090c0426cf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -40,6 +40,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public class DefaultExchangeFactory implements ExchangeFactory { private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class); + public static final String DEFAULT_DLE_NAME_SUFFIX = "_DLE"; private Map<AMQShortString, ExchangeType<? extends Exchange>> _exchangeClassMap = new HashMap<AMQShortString, ExchangeType<? extends Exchange>>(); private final VirtualHost _host; @@ -122,7 +123,7 @@ public class DefaultExchangeFactory implements ExchangeFactory if (exchangeType == null) { _logger.error("No such custom exchange class found: \""+String.valueOf(className)+"\""); - return; + continue; } Class<? extends ExchangeType> exchangeTypeClass = exchangeType.getClass(); ExchangeType<? extends ExchangeType> type = exchangeTypeClass.newInstance(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index 0e7459498a..8d2dee5aaa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -27,7 +27,9 @@ import java.util.concurrent.ConcurrentMap; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -87,12 +89,22 @@ public class DefaultExchangeRegistry implements ExchangeRegistry public void unregisterExchange(AMQShortString name, boolean inUse) throws AMQException { - // Check access - if (!_host.getSecurityManager().authoriseDelete(_exchangeMap.get(name))) + final Exchange exchange = _exchangeMap.get(name); + if (exchange == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Unknown exchange " + name, null); + } + + if (ExchangeDefaults.DEFAULT_EXCHANGE_NAME.equals(name)) + { + throw new AMQException(AMQConstant.NOT_ALLOWED, "Cannot unregister the default exchange", null); + } + + if (!_host.getSecurityManager().authoriseDelete(exchange)) { throw new AMQSecurityException(); } - + // TODO: check inUse argument Exchange e = _exchangeMap.remove(name); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index bd75f7bc51..76f86ea1b4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -117,7 +117,7 @@ public class FanoutExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey, AMQQueue queue) { - return _queues.contains(queue); + return _queues.containsKey(queue); } public boolean isBound(AMQShortString routingKey) @@ -129,7 +129,7 @@ public class FanoutExchange extends AbstractExchange public boolean isBound(AMQQueue queue) { - return _queues.contains(queue); + return _queues.containsKey(queue); } public boolean hasBindings() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java index 66c9b5b552..caed8f4b94 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java @@ -20,12 +20,21 @@ */ package org.apache.qpid.server.exchange; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; import javax.management.JMException; +import javax.management.MBeanException; import javax.management.openmbean.*; + +import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Map; @@ -94,5 +103,48 @@ final class HeadersExchangeMBean extends AbstractExchangeMBean<HeadersExchange> return bindingList; } + @Override + public void createNewBinding(String queueName, String binding) throws JMException + { + VirtualHost vhost = getExchange().getVirtualHost(); + AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the virtualhost."); + } + + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + + final Map<String,Object> arguments = new HashMap<String, Object>(); + final String[] bindings = binding.split(","); + for (int i = 0; i < bindings.length; i++) + { + final String[] keyAndValue = bindings[i].split("="); + if (keyAndValue == null || keyAndValue.length == 0 || keyAndValue.length > 2 || keyAndValue[0].length() == 0) + { + throw new JMException("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\" "); + } + + if(keyAndValue.length == 1) + { + //no value was given, only a key. Use an empty value to signal match on key presence alone + arguments.put(keyAndValue[0], ""); + } + else + { + arguments.put(keyAndValue[0], keyAndValue[1]); + } + } + try + { + vhost.getBindingFactory().addBinding(binding,queue,getExchange(),arguments); + } + catch (AMQException ex) + { + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error creating new binding " + binding); + } + CurrentActor.remove(); + } } // End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java index c5f2d1e808..5a5ef5e6c5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; import java.util.concurrent.atomic.AtomicLong; @@ -59,9 +58,8 @@ public class BytesOnlyCreditManager extends AbstractFlowCreditManager return _bytesCredit.get() > 0L; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { - final long msgSize = msg.getSize(); if(hasCredit()) { if(_bytesCredit.addAndGet(-msgSize) >= 0) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java index b47f986155..c6771177ac 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; public class CreditCreditManager extends AbstractFlowCreditManager implements FlowCreditManager_0_10 { @@ -118,7 +117,7 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl return (_bytesCredit != 0L && _messageCredit != 0L); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(long msgSize) { if(_messageCredit >= 0L) { @@ -130,10 +129,10 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl return true; } - else if(msg.getSize() <= _bytesCredit) + else if(msgSize <= _bytesCredit) { _messageCredit--; - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } @@ -151,9 +150,9 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl } else if(_bytesCredit >= 0L) { - if(msg.getSize() <= _bytesCredit) + if(msgSize <= _bytesCredit) { - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java index bec51d361d..8a80262983 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -41,6 +40,6 @@ public interface FlowCreditManager public boolean hasCredit(); - public boolean useCreditForMessage(ServerMessage msg); + public boolean useCreditForMessage(long msgSize); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java index 901b71fd1f..6fcc687440 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -47,7 +46,7 @@ public class LimitlessCreditManager extends AbstractFlowCreditManager implements return true; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java index 19a9ac1d23..0e6ce70a60 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -62,7 +61,7 @@ public class MessageAndBytesCreditManager extends AbstractFlowCreditManager impl return (_messageCredit > 0L) && ( _bytesCredit > 0L ); } - public synchronized boolean useCreditForMessage(ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCredit == 0L) { @@ -71,7 +70,6 @@ public class MessageAndBytesCreditManager extends AbstractFlowCreditManager impl } else { - final long msgSize = msg.getSize(); if(msgSize > _bytesCredit) { setSuspended(true); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java index a386f66b11..9b7c40e923 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; import java.util.concurrent.atomic.AtomicLong; @@ -61,7 +60,7 @@ public class MessageOnlyCreditManager extends AbstractFlowCreditManager implemen return _messageCredit.get() > 0L; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { if(hasCredit()) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java index 026804439c..a193f8fae4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; public class Pre0_10CreditManager extends AbstractFlowCreditManager implements FlowCreditManager { @@ -133,7 +132,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F && (_messageCreditLimit == 0L || _messageCredit > 0); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCreditLimit != 0L) { @@ -147,10 +146,10 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + if((_bytesCredit >= msgSize) || (_bytesCredit == _bytesCreditLimit)) { _messageCredit--; - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } @@ -176,9 +175,9 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + if((_bytesCredit >= msgSize) || (_bytesCredit == _bytesCreditLimit)) { - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java index 10f578551a..a0c2e9f977 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; +import org.apache.log4j.Logger; public class WindowCreditManager extends AbstractFlowCreditManager implements FlowCreditManager_0_10 { + private static final Logger LOGGER = Logger.getLogger(WindowCreditManager.class); + private volatile long _bytesCreditLimit; private volatile long _messageCreditLimit; @@ -43,6 +45,15 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } + public long getBytesCreditLimit() + { + return _bytesCreditLimit; + } + + public long getMessageCreditLimit() + { + return _messageCreditLimit; + } public synchronized void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit) { @@ -70,31 +81,30 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl public synchronized void restoreCredit(final long messageCredit, final long bytesCredit) { + _messageUsed -= messageCredit; + if(_messageUsed < 0L) + { + LOGGER.error("Message credit used value was negative: "+ _messageUsed); + _messageUsed = 0; + } + boolean notifyIncrease = true; + if(_messageCreditLimit > 0L) { notifyIncrease = (_messageUsed != _messageCreditLimit); - _messageUsed -= messageCredit; - - //TODO log warning - if(_messageUsed < 0L) - { - _messageUsed = 0; - } } - + _bytesUsed -= bytesCredit; + if(_bytesUsed < 0L) + { + LOGGER.error("Bytes credit used value was negative: "+ _messageUsed); + _bytesUsed = 0; + } if(_bytesCreditLimit > 0L) { notifyIncrease = notifyIncrease && bytesCredit>0; - _bytesUsed -= bytesCredit; - - //TODO log warning - if(_bytesUsed < 0L) - { - _bytesUsed = 0; - } if(notifyIncrease) { @@ -102,10 +112,7 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } } - - setSuspended(!hasCredit()); - } @@ -116,7 +123,7 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl && (_messageCreditLimit < 0L || _messageCreditLimit > _messageUsed); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCreditLimit >= 0L) { @@ -128,10 +135,10 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl return true; } - else if(_bytesUsed + msg.getSize() <= _bytesCreditLimit) + else if(_bytesUsed + msgSize <= _bytesCreditLimit) { _messageUsed++; - _bytesUsed += msg.getSize(); + _bytesUsed += msgSize; return true; } @@ -149,9 +156,9 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } else if(_bytesCreditLimit >= 0L) { - if(_bytesUsed + msg.getSize() <= _bytesCreditLimit) + if(_bytesUsed + msgSize <= _bytesCreditLimit) { - _bytesUsed += msg.getSize(); + _bytesUsed += msgSize; return true; } @@ -169,18 +176,6 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } - public void stop() - { - if(_bytesCreditLimit > 0) - { - _bytesCreditLimit = 0; - } - if(_messageCreditLimit > 0) - { - _messageCreditLimit = 0; - } - - } public synchronized void addCredit(long count, long bytes) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index 14ce85530e..bbb009003c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -127,16 +127,15 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB final ClientDeliveryMethod getDeliveryMethod = new ClientDeliveryMethod() { - int _msg; - public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) throws AMQException { - singleMessageCredit.useCreditForMessage(entry.getMessage()); + singleMessageCredit.useCreditForMessage(entry.getMessage().getSize()); if(entry.getMessage() instanceof AMQMessage) { session.getProtocolOutputConverter().writeGetOk(entry, channel.getChannelId(), deliveryTag, queue.getMessageCount()); + entry.incrementDeliveryCount(); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java index 62dd76f832..0ea88e4ab6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -59,7 +59,6 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR { _logger.debug("Rejecting:" + body.getDeliveryTag() + ": Requeue:" + body.getRequeue() + - //": Resend:" + evt.getMethod().resend + " on channel:" + channel.debugIdentity()); } @@ -70,26 +69,23 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR if (message == null) { _logger.warn("Dropping reject request as message is null for tag:" + deliveryTag); -// throw evt.getMethod().getChannelException(AMQConstant.NOT_FOUND, "Delivery Tag(" + deliveryTag + ")not known"); } else { if (message.isQueueDeleted()) { - _logger.warn("Message's Queue as already been purged, unable to Reject. " + - "Dropping message should use Dead Letter Queue"); + _logger.warn("Message's Queue has already been purged, dropping message"); message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); if(message != null) { message.discard(); } - //sendtoDeadLetterQueue(msg) return; } if (message.getMessage() == null) { - _logger.warn("Message as already been purged, unable to Reject."); + _logger.warn("Message has already been purged, unable to Reject."); return; } @@ -98,27 +94,44 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR { _logger.debug("Rejecting: DT:" + deliveryTag + "-" + message.getMessage() + ": Requeue:" + body.getRequeue() + - //": Resend:" + evt.getMethod().resend + " on channel:" + channel.debugIdentity()); } - // If we haven't requested message to be resent to this consumer then reject it from ever getting it. - //if (!evt.getMethod().resend) - { - message.reject(); - } + message.reject(); if (body.getRequeue()) { channel.requeue(deliveryTag); + + //this requeue represents a message rejected from the pre-dispatch queue + //therefore we need to amend the delivery counter. + message.decrementDeliveryCount(); } else { - _logger.warn("Dropping message as requeue not required and there is no dead letter queue"); - message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); - //sendtoDeadLetterQueue(AMQMessage message) -// message.queue = channel.getDefaultDeadLetterQueue(); -// channel.requeue(deliveryTag); + final boolean maxDeliveryCountEnabled = channel.isMaxDeliveryCountEnabled(deliveryTag); + _logger.debug("maxDeliveryCountEnabled: " + maxDeliveryCountEnabled + " deliveryTag " + deliveryTag); + if (maxDeliveryCountEnabled) + { + final boolean deliveredTooManyTimes = channel.isDeliveredTooManyTimes(deliveryTag); + _logger.debug("deliveredTooManyTimes: " + deliveredTooManyTimes + " deliveryTag " + deliveryTag); + if (deliveredTooManyTimes) + { + channel.deadLetter(body.getDeliveryTag()); + } + else + { + //this requeue represents a message rejected because of a recover/rollback that we + //are not ready to DLQ. We rely on the reject command to resend from the unacked map + //and therefore need to increment the delivery counter so we cancel out the effect + //of the AMQChannel#resend() decrement. + message.incrementDeliveryCount(); + } + } + else + { + channel.deadLetter(body.getDeliveryTag()); + } } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java index 8391a4b184..f2119f7faa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java @@ -25,8 +25,10 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.QueueUnbindBody; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.Exchange; @@ -62,7 +64,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB final AMQQueue queue; - final AMQShortString routingKey; + final AMQShortString routingKey; if (body.getQueue() == null) { @@ -114,10 +116,21 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey); } - MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createQueueUnbindOkBody(); + final MethodRegistry registry = session.getMethodRegistry(); + final AMQMethodBody responseBody; + if (registry instanceof MethodRegistry_0_9) + { + responseBody = ((MethodRegistry_0_9)registry).createQueueUnbindOkBody(); + } + else if (registry instanceof MethodRegistry_0_91) + { + responseBody = ((MethodRegistry_0_91)registry).createQueueUnbindOkBody(); + } + else + { + // 0-8 does not support QueueUnbind + throw new AMQException(AMQConstant.COMMAND_INVALID, "QueueUnbind not present in AMQP version: " + session.getProtocolVersion(), null); + } session.writeFrame(responseBody.generateFrame(channelId)); - - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java index 4643dee0a3..20ba3af458 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java @@ -72,7 +72,12 @@ public class TxRollbackHandler implements StateAwareMethodListener<TxRollbackBod }; channel.rollback(task); - + + //Now resend all the unacknowledged messages back to the original subscribers. + //(Must be done after the TxnRollback-ok response). + // Why, are we not allowed to send messages back to client before the ok method? + channel.resend(false); + } catch (AMQException e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties index ed8c0d0ce9..b5df212904 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties @@ -32,3 +32,7 @@ FLOW_REMOVED = CHN-1006 : Flow Control Removed # 0 - time in milliseconds OPEN_TXN = CHN-1007 : Open Transaction : {0,number} ms IDLE_TXN = CHN-1008 : Idle Transaction : {0,number} ms + +DISCARDMSG_NOALTEXCH = CHN-1009 : Discarded message : {0,number} as no alternate exchange configured for queue : {1} routing key : {2} +DISCARDMSG_NOROUTE = CHN-1010 : Discarded message : {0,number} as no binding on alternate exchange : {1} +DEADLETTERMSG = CHN-1011 : Message : {0,number} moved to dead letter queue : {1} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java index c4ffcd26bf..6c9d6e39de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java @@ -52,8 +52,6 @@ public abstract class AMQManagedObject extends DefaultManagedObject */ protected long _notificationSequenceNumber = 0; - protected MBeanInfo _mbeanInfo; - protected LogActor _logActor; protected AMQManagedObject(Class<?> managementInterface, String typeName) @@ -63,27 +61,8 @@ public abstract class AMQManagedObject extends DefaultManagedObject // CurrentActor will be defined as these objects are created during // broker startup. _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger()); - buildMBeanInfo(); - } - - @Override - public MBeanInfo getMBeanInfo() - { - return _mbeanInfo; - } - - private void buildMBeanInfo() throws NotCompliantMBeanException - { - _mbeanInfo = new MBeanInfo(this.getClass().getName(), - MBeanIntrospector.getMBeanDescription(this.getClass()), - MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()), - MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()), - MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()), - this.getNotificationInfo()); } - - // notification broadcaster implementation public void addNotificationListener(NotificationListener listener, @@ -99,8 +78,5 @@ public abstract class AMQManagedObject extends DefaultManagedObject _broadcaster.removeNotificationListener(listener); } - public MBeanNotificationInfo[] getNotificationInfo() - { - return null; - } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java new file mode 100644 index 0000000000..68350a1632 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java @@ -0,0 +1,71 @@ +package org.apache.qpid.server.management; + +import javax.management.Notification; + +import javax.management.JMException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.monitor.MonitorNotification; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularType; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +public abstract class AbstractAMQManagedConnectionObject extends AMQManagedObject implements ManagedConnection +{ + protected final String _name; + + protected static final OpenType[] _channelAttributeTypes = { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN }; + protected static final CompositeType _channelType; + protected static final TabularType _channelsType; + + protected static final String BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR = + "Broker Management Console has closed the connection."; + + static + { + try + { + _channelType = new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), + COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), _channelAttributeTypes); + _channelsType = new TabularType("Channels", "Channels", _channelType, (String[]) TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); + } + catch (JMException ex) + { + // This is not expected to ever occur. + throw new RuntimeException("Got JMException in static initializer.", ex); + } + } + + protected AbstractAMQManagedConnectionObject(final String remoteAddress) throws NotCompliantMBeanException + { + super(ManagedConnection.class, ManagedConnection.TYPE); + _name = "anonymous".equals(remoteAddress) ? (remoteAddress + hashCode()) : remoteAddress; + } + + @Override + public String getObjectInstanceName() + { + return ObjectName.quote(_name); + } + + public void notifyClients(String notificationMsg) + { + final Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, + System.currentTimeMillis(), notificationMsg); + _broadcaster.sendNotification(n); + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() + { + String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; + String name = MonitorNotification.class.getName(); + String description = "Channel count has reached threshold value"; + MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); + + return new MBeanNotificationInfo[] { info1 }; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java index e44b8c41cb..0c3a5fc571 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java @@ -21,6 +21,8 @@ package org.apache.qpid.server.management; import javax.management.JMException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; @@ -28,7 +30,6 @@ import javax.management.StandardMBean; import org.apache.log4j.Logger; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; /** * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful @@ -37,11 +38,13 @@ import org.apache.qpid.server.registry.IApplicationRegistry; */ public abstract class DefaultManagedObject extends StandardMBean implements ManagedObject { - private static final Logger LOGGER = Logger.getLogger(ApplicationRegistry.class); + private static final Logger LOGGER = Logger.getLogger(DefaultManagedObject.class); - private Class<?> _managementInterface; + private final Class<?> _managementInterface; - private String _typeName; + private final String _typeName; + + private final MBeanInfo _mbeanInfo; private ManagedObjectRegistry _registry; @@ -51,6 +54,13 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana super(managementInterface); _managementInterface = managementInterface; _typeName = typeName; + _mbeanInfo = buildMBeanInfo(); + } + + @Override + public MBeanInfo getMBeanInfo() + { + return _mbeanInfo; } public String getType() @@ -98,7 +108,6 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return getObjectInstanceName() + "[" + getType() + "]"; } - /** * Created the ObjectName as per the JMX Specs * @return ObjectName @@ -161,4 +170,18 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return ""; } + private MBeanInfo buildMBeanInfo() throws NotCompliantMBeanException + { + return new MBeanInfo(this.getClass().getName(), + MBeanIntrospector.getMBeanDescription(this.getClass()), + MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()), + MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()), + MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()), + this.getNotificationInfo()); + } + + public MBeanNotificationInfo[] getNotificationInfo() + { + return null; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java index b44964f176..8583e8d57b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -38,10 +38,13 @@ import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; +import javax.management.Notification; import javax.management.NotificationFilterSupport; import javax.management.NotificationListener; import javax.management.ObjectName; @@ -49,11 +52,13 @@ import javax.management.remote.JMXConnectionNotification; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXServiceURL; import javax.management.remote.MBeanServerForwarder; +import javax.management.remote.rmi.RMIConnection; import javax.management.remote.rmi.RMIConnectorServer; import javax.management.remote.rmi.RMIJRMPServerImpl; import javax.management.remote.rmi.RMIServerImpl; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; +import javax.security.auth.Subject; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; @@ -63,6 +68,7 @@ import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; /** * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no @@ -223,7 +229,40 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's. */ - final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env); + final Map<String, String> connectionIdUsernameMap = new ConcurrentHashMap<String, String>(); + final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env) + { + + /** + * Override makeClient so we can cache the username of the client in a Map keyed by connectionId. + * ConnectionId is guaranteed to be unique per client connection, according to the JMS spec. + * An instance of NotificationListener (mapCleanupListener) will be responsible for removing these Map + * entries. + * + * @see javax.management.remote.rmi.RMIJRMPServerImpl#makeClient(java.lang.String, javax.security.auth.Subject) + */ + @Override + protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException + { + final RMIConnection makeClient = super.makeClient(connectionId, subject); + final UsernamePrincipal usernamePrincipalFromSubject = UsernamePrincipal.getUsernamePrincipalFromSubject(subject); + connectionIdUsernameMap.put(connectionId, usernamePrincipalFromSubject.getName()); + return makeClient; + } + }; + + // Create a Listener responsible for removing the map entries add by the #makeClient entry above. + final NotificationListener mapCleanupListener = new NotificationListener() + { + + @Override + public void handleNotification(Notification notification, Object handback) + { + final String connectionId = ((JMXConnectionNotification) notification).getConnectionId(); + connectionIdUsernameMap.remove(connectionId); + } + }; + String localHost; try { @@ -295,13 +334,26 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance(); _cs.setMBeanServerForwarder(mbsf); - NotificationFilterSupport filter = new NotificationFilterSupport(); - filter.enableType(JMXConnectionNotification.OPENED); - filter.enableType(JMXConnectionNotification.CLOSED); - filter.enableType(JMXConnectionNotification.FAILED); + // Get the handler that is used by the above MBInvocationHandler Proxy. - // which is the MBeanInvocationHandlerImpl and so also a NotificationListener - _cs.addNotificationListener((NotificationListener) Proxy.getInvocationHandler(mbsf), filter, null); + // which is the MBeanInvocationHandlerImpl and so also a NotificationListener. + final NotificationListener invocationHandler = (NotificationListener) Proxy.getInvocationHandler(mbsf); + + // Install a notification listener on OPENED, CLOSED, and FAILED, + // passing the map of connection-ids to usernames as hand-back data. + final NotificationFilterSupport invocationHandlerFilter = new NotificationFilterSupport(); + invocationHandlerFilter.enableType(JMXConnectionNotification.OPENED); + invocationHandlerFilter.enableType(JMXConnectionNotification.CLOSED); + invocationHandlerFilter.enableType(JMXConnectionNotification.FAILED); + _cs.addNotificationListener(invocationHandler, invocationHandlerFilter, connectionIdUsernameMap); + + // Install a second notification listener on CLOSED AND FAILED only to remove the entry from the + // Map. Here we rely on the fact that JMX will call the listeners in the order in which they are + // installed. + final NotificationFilterSupport mapCleanupHandlerFilter = new NotificationFilterSupport(); + mapCleanupHandlerFilter.enableType(JMXConnectionNotification.CLOSED); + mapCleanupHandlerFilter.enableType(JMXConnectionNotification.FAILED); + _cs.addNotificationListener(mapCleanupListener, mapCleanupHandlerFilter, null); _cs.start(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java index 169195304c..40a221e0ba 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.AccessControlContext; import java.security.AccessController; +import java.util.Map; import java.util.Set; import javax.management.Attribute; @@ -45,6 +46,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; @@ -56,22 +58,54 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati { private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class); + private final IApplicationRegistry _appRegistry = ApplicationRegistry.getInstance(); private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate"; private MBeanServer _mbs; - private static ManagementActor _logActor; - + private final ManagementActor _logActor = new ManagementActor(_appRegistry.getRootMessageLogger()); + private final boolean _managementRightsInferAllAccess = + _appRegistry.getConfiguration().getManagementRightsInferAllAccess(); + public static MBeanServerForwarder newProxyInstance() { final InvocationHandler handler = new MBeanInvocationHandlerImpl(); final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class }; - - _logActor = new ManagementActor(ApplicationRegistry.getInstance().getRootMessageLogger()); - Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler); return MBeanServerForwarder.class.cast(proxy); } + private boolean invokeDirectly(String methodName, Object[] args, Subject subject) + { + // Allow operations performed locally on behalf of the connector server itself + if (subject == null) + { + return true; + } + + if (args == null || DELEGATE.equals(args[0])) + { + return true; + } + + // Allow querying available object names + if (methodName.equals("queryNames")) + { + return true; + } + + if (args[0] instanceof ObjectName) + { + ObjectName mbean = (ObjectName) args[0]; + + if(!DefaultManagedObject.DOMAIN.equalsIgnoreCase(mbean.getDomain())) + { + return true; + } + } + + return false; + } + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final String methodName = getMethodName(method, args); @@ -95,36 +129,24 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati return null; } + // Restrict access to "createMBean" and "unregisterMBean" to any user + if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) + { + _logger.debug("User trying to create or unregister an MBean"); + throw new SecurityException("Access denied: " + methodName); + } + // Retrieve Subject from current AccessControlContext AccessControlContext acc = AccessController.getContext(); Subject subject = Subject.getSubject(acc); try { - // Allow operations performed locally on behalf of the connector server itself - if (subject == null) + if(invokeDirectly(methodName, args, subject)) { return method.invoke(_mbs, args); } - - if (args == null || DELEGATE.equals(args[0])) - { - return method.invoke(_mbs, args); - } - - // Restrict access to "createMBean" and "unregisterMBean" to any user - if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) - { - _logger.debug("User trying to create or unregister an MBean"); - throw new SecurityException("Access denied: " + methodName); - } - - // Allow querying available object names - if (methodName.equals("queryNames")) - { - return method.invoke(_mbs, args); - } - + // Retrieve JMXPrincipal from Subject Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); if (principals == null || principals.isEmpty()) @@ -134,23 +156,23 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati // Save the subject SecurityManager.setThreadSubject(subject); - + // Get the component, type and impact, which may be null String type = getType(method, args); String vhost = getVirtualHost(method, args); int impact = getImpact(method, args); - + // Get the security manager for the virtual host (if set) SecurityManager security; if (vhost == null) { - security = ApplicationRegistry.getInstance().getSecurityManager(); + security = _appRegistry.getSecurityManager(); } else { - security = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager(); + security = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager(); } - + if (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO) { // Check for read-only method invocation permission @@ -159,25 +181,33 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati throw new SecurityException("Permission denied: Access " + methodName); } } - else if (isUpdateMethod(methodName)) - { - // Check for setting properties permission - if (!security.authoriseMethod(Operation.UPDATE, type, methodName)) - { - throw new SecurityException("Permission denied: Update " + methodName); - } - } - else - { - // Check for invoking/executing method action/operation permission - if (!security.authoriseMethod(Operation.EXECUTE, type, methodName)) - { - throw new SecurityException("Permission denied: Execute " + methodName); - } - } - - // Actually invoke the method - return method.invoke(_mbs, args); + else + { + // Check for setting properties permission + if (!security.authoriseMethod(Operation.UPDATE, type, methodName)) + { + throw new SecurityException("Permission denied: Update " + methodName); + } + } + + boolean oldAccessChecksDisabled = false; + if(_managementRightsInferAllAccess) + { + oldAccessChecksDisabled = SecurityManager.setAccessChecksDisabled(true); + } + + try + { + // Actually invoke the method + return method.invoke(_mbs, args); + } + finally + { + if(_managementRightsInferAllAccess) + { + SecurityManager.setAccessChecksDisabled(oldAccessChecksDisabled); + } + } } catch (InvocationTargetException e) { @@ -290,28 +320,44 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati return (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("is")); } - - private boolean isUpdateMethod(String methodName) - { - //handle standard set methods from MBeanServer - return methodName.startsWith("set"); - } - - public void handleNotification(Notification notification, Object handback) + /** + * Receives notifications from the MBeanServer. + */ + public void handleNotification(final Notification notification, final Object handback) { assert notification instanceof JMXConnectionNotification; - // only RMI Connections are serviced here, Local API atta - // rmi://169.24.29.116 guest 3 - String[] connectionData = ((JMXConnectionNotification) notification).getConnectionId().split(" "); - String user = connectionData[1]; + final String connectionId = ((JMXConnectionNotification) notification).getConnectionId(); + final String type = notification.getType(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Notification connectionId : " + connectionId + " type : " + type + + " Notification handback : " + handback); + } + + // Normally JMXManagedObjectRegistry provides a Map as handback data containing a map + // between connection id and username. + String user = null; + if (handback != null && handback instanceof Map) + { + final Map<String, String> connectionIdUsernameMap = (Map<String, String>) handback; + user = connectionIdUsernameMap.get(connectionId); + } + + // If user is still null, fallback to an unordered list of Principals from the connection id. + if (user == null) + { + final String[] splitConnectionId = connectionId.split(" "); + user = splitConnectionId[1]; + } - if (notification.getType().equals(JMXConnectionNotification.OPENED)) + if (JMXConnectionNotification.OPENED.equals(type)) { _logActor.message(ManagementConsoleMessages.OPEN(user)); } - else if (notification.getType().equals(JMXConnectionNotification.CLOSED) || - notification.getType().equals(JMXConnectionNotification.FAILED)) + else if (JMXConnectionNotification.CLOSED.equals(type) || + JMXConnectionNotification.FAILED.equals(type)) { _logActor.message(ManagementConsoleMessages.CLOSE(user)); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java index 0e0b18aa2f..4fcbaa237e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.message; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.AMQChannel; @@ -65,7 +66,6 @@ public class AMQMessage extends AbstractServerMessageImpl WeakReference<AMQChannel> _channelRef; - public AMQMessage(StoredMessage<MessageMetaData> handle) { this(handle, null); @@ -122,7 +122,15 @@ public class AMQMessage extends AbstractServerMessageImpl public String getRoutingKey() { - // TODO + MessageMetaData messageMetaData = getMessageMetaData(); + if (messageMetaData != null) + { + AMQShortString routingKey = messageMetaData.getMessagePublishInfo().getRoutingKey(); + if (routingKey != null) + { + return routingKey.asString(); + } + } return null; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java index 186bb8601c..80c28332c0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.server.message; import java.util.concurrent.atomic.AtomicInteger; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java index 31cf223428..2f30f260c9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java @@ -79,7 +79,7 @@ class MessageTransferHeader implements AMQMessageHeader public byte getPriority() { - MessageDeliveryPriority priority = _deliveryProps == null + MessageDeliveryPriority priority = _deliveryProps == null || !_deliveryProps.hasPriority() ? MessageDeliveryPriority.MEDIUM : _deliveryProps.getPriority(); return (byte) priority.getValue(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties index aaab4f76cc..badeffca05 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties @@ -75,6 +75,7 @@ org.apache.qpid.server.exchange=0.0.0 org.apache.qpid.server.logging=0.0.0 org.apache.qpid.server.logging.actors=0.0.0 org.apache.qpid.server.logging.subjects=0.0.0 +org.apache.qpid.server.message=0.0.0 org.apache.qpid.server.management=0.0.0 org.apache.qpid.server.persistent=0.0.0 org.apache.qpid.server.plugins=0.0.0 diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index b960ce8608..4e5088808a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.protocol; +import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -36,36 +37,17 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; - import javax.management.JMException; import javax.security.auth.Subject; import javax.security.sasl.SaslServer; - import org.apache.log4j.Logger; import org.apache.qpid.AMQChannelException; import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.codec.AMQDecoder; import org.apache.qpid.common.ClientProperties; -import org.apache.qpid.framing.AMQBody; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQProtocolHeaderException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ChannelCloseBody; -import org.apache.qpid.framing.ChannelCloseOkBody; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.HeartbeatBody; -import org.apache.qpid.framing.MethodDispatcher; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; @@ -87,11 +69,15 @@ import org.apache.qpid.server.management.Managable; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.Sender; @@ -139,7 +125,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr /* AMQP Version for this session */ private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion(); - + private MethodRegistry _methodRegistry = MethodRegistry.getMethodRegistry(_protocolVersion); private FieldTable _clientProperties; private final List<Task> _taskList = new CopyOnWriteArrayList<Task>(); @@ -173,6 +159,9 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private NetworkConnection _network; private Sender<ByteBuffer> _sender; + private volatile boolean _deferFlush; + private long _lastReceivedTime; + public ManagedObject getManagedObject() { return _managedObject; @@ -240,14 +229,29 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr return _closing.get(); } + public synchronized void flushBatched() + { + _sender.flush(); + } + + + public ClientDeliveryMethod createDeliveryMethod(int channelId) + { + return new WriteDeliverMethod(channelId); + } + public void received(final ByteBuffer msg) { - _lastIoTime = System.currentTimeMillis(); + final long arrivalTime = System.currentTimeMillis(); + _lastReceivedTime = arrivalTime; + _lastIoTime = arrivalTime; try { final ArrayList<AMQDataBlock> dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - for (AMQDataBlock dataBlock : dataBlocks) + final int len = dataBlocks.size(); + for (int i = 0; i < len; i++) { + AMQDataBlock dataBlock = dataBlocks.get(i); try { dataBlockReceived(dataBlock); @@ -347,7 +351,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr } } - private void protocolInitiationReceived(ProtocolInitiation pi) + private synchronized void protocolInitiationReceived(ProtocolInitiation pi) { // this ensures the codec never checks for a PI message again (_codecFactory.getDecoder()).setExpectProtocolInitiation(false); @@ -524,12 +528,15 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr */ public synchronized void writeFrame(AMQDataBlock frame) { - _lastSent = frame; + final ByteBuffer buf = asByteBuffer(frame); - _lastIoTime = System.currentTimeMillis(); _writtenBytes += buf.remaining(); _sender.send(buf); - _sender.flush(); + _lastIoTime = System.currentTimeMillis(); + if(!_deferFlush) + { + _sender.flush(); + } } public AMQShortString getContextKey() @@ -918,7 +925,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private void setProtocolVersion(ProtocolVersion pv) { _protocolVersion = pv; - + _methodRegistry = MethodRegistry.getMethodRegistry(_protocolVersion); _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); _dispatcher = ServerMethodDispatcherImpl.createMethodDispatcher(_stateManager, _protocolVersion); } @@ -1023,7 +1030,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public MethodRegistry getMethodRegistry() { - return MethodRegistry.getMethodRegistry(getProtocolVersion()); + return _methodRegistry; } public MethodDispatcher getMethodDispatcher() @@ -1052,7 +1059,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr // Nothing } - public void writerIdle() + public synchronized void writerIdle() { _sender.send(asByteBuffer(HeartbeatBody.FRAME)); } @@ -1109,6 +1116,11 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr return _lastIoTime; } + public long getLastReceivedTime() + { + return _lastReceivedTime; + } + public ProtocolSessionIdentifier getSessionIdentifier() { return _sessionIdentifier; @@ -1395,16 +1407,220 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr _statisticsEnabled = enabled; } - @Override public boolean isSessionNameUnique(byte[] name) { // 0-8/0-9/0-9-1 sessions don't have names return true; } - @Override + public void setDeferFlush(boolean deferFlush) + { + _deferFlush = deferFlush; + } + + + public String getUserName() { return getAuthorizedPrincipal().getName(); } + + private static class ByteBufferOutputStream extends OutputStream + { + + + private final ByteBuffer _buf; + + public ByteBufferOutputStream(ByteBuffer buf) + { + _buf = buf; + } + + @Override + public void write(int b) throws IOException + { + _buf.put((byte) b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + _buf.put(b, off, len); + } + } + + public final class WriteDeliverMethod + implements ClientDeliveryMethod + { + private final int _channelId; + + public WriteDeliverMethod(int channelId) + { + _channelId = channelId; + } + + public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) + throws AMQException + { + registerMessageDelivered(entry.getMessage().getSize()); + _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); + entry.incrementDeliveryCount(); + } + + } + + private static class BytesDataOutput implements DataOutput + { + int _pos = 0; + byte[] _buf; + + public BytesDataOutput(byte[] buf) + { + _buf = buf; + } + + public void setBuffer(byte[] buf) + { + _buf = buf; + _pos = 0; + } + + public void reset() + { + _pos = 0; + } + + public int length() + { + return _pos; + } + + public void write(int b) + { + _buf[_pos++] = (byte) b; + } + + public void write(byte[] b) + { + System.arraycopy(b, 0, _buf, _pos, b.length); + _pos+=b.length; + } + + + public void write(byte[] b, int off, int len) + { + System.arraycopy(b, off, _buf, _pos, len); + _pos+=len; + + } + + public void writeBoolean(boolean v) + { + _buf[_pos++] = v ? (byte) 1 : (byte) 0; + } + + public void writeByte(int v) + { + _buf[_pos++] = (byte) v; + } + + public void writeShort(int v) + { + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeChar(int v) + { + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeInt(int v) + { + _buf[_pos++] = (byte) (v >>> 24); + _buf[_pos++] = (byte) (v >>> 16); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeLong(long v) + { + _buf[_pos++] = (byte) (v >>> 56); + _buf[_pos++] = (byte) (v >>> 48); + _buf[_pos++] = (byte) (v >>> 40); + _buf[_pos++] = (byte) (v >>> 32); + _buf[_pos++] = (byte) (v >>> 24); + _buf[_pos++] = (byte) (v >>> 16); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte)v; + } + + public void writeFloat(float v) + { + writeInt(Float.floatToIntBits(v)); + } + + public void writeDouble(double v) + { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeBytes(String s) + { + int len = s.length(); + for (int i = 0 ; i < len ; i++) + { + _buf[_pos++] = ((byte)s.charAt(i)); + } + } + + public void writeChars(String s) + { + int len = s.length(); + for (int i = 0 ; i < len ; i++) + { + int v = s.charAt(i); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + } + + public void writeUTF(String s) + { + int strlen = s.length(); + + int pos = _pos; + _pos+=2; + + + for (int i = 0; i < strlen; i++) + { + int c = s.charAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) + { + c = s.charAt(i); + _buf[_pos++] = (byte) c; + + } + else if (c > 0x07FF) + { + _buf[_pos++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + _buf[_pos++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + _buf[_pos++] = (byte) (0x80 | (c & 0x3F)); + } + else + { + _buf[_pos++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + _buf[_pos++] = (byte) (0x80 | (c & 0x3F)); + } + } + + int len = _pos - (pos + 2); + + _buf[pos++] = (byte) (len >>> 8); + _buf[pos] = (byte) len; + } + + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index c1b5b02f8f..dfba10750c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -49,6 +50,14 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth boolean isClosing(); + void flushBatched(); + + void setDeferFlush(boolean defer); + + ClientDeliveryMethod createDeliveryMethod(int channelId); + + long getLastReceivedTime(); + public static final class ProtocolSessionIdentifier { private final Object _sessionIdentifier; @@ -77,15 +86,6 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth } /** - * Called when a protocol data block is received - * - * @param message the data block that has been received - * - * @throws Exception if processing the datablock fails - */ - void dataBlockReceived(AMQDataBlock message) throws Exception; - - /** * Get the context key associated with this session. Context key is described in the AMQ protocol specification (RFC * 6). * @@ -234,4 +234,5 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth List<AMQChannel> getChannels(); void mgmtCloseChannel(int channelId); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java index 16d99de492..8d39420631 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -39,89 +39,44 @@ package org.apache.qpid.server.protocol; import java.util.Date; import java.util.List; - import javax.management.JMException; import javax.management.MBeanException; -import javax.management.MBeanNotificationInfo; import javax.management.NotCompliantMBeanException; -import javax.management.Notification; -import javax.management.ObjectName; -import javax.management.monitor.MonitorNotification; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; -import javax.management.openmbean.TabularType; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ConnectionCloseBody; import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; import org.apache.qpid.server.management.ManagedObject; /** * This MBean class implements the management interface. In order to make more attributes, operations and notifications * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. */ -@MBeanDescription("Management Bean for an AMQ Broker Connection") -public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection +@MBeanDescription("Management Bean for an AMQ Broker 0-9-1/0-9/0-8 Connections") +public class AMQProtocolSessionMBean extends AbstractAMQManagedConnectionObject { private AMQProtocolSession _protocolSession = null; - private String _name = null; - // openmbean data types for representing the channel attributes - - private static final OpenType[] _channelAttributeTypes = - { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN }; - private static CompositeType _channelType = null; // represents the data type for channel data - private static TabularType _channelsType = null; // Data type for list of channels type private static final AMQShortString BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION = - new AMQShortString("Broker Management Console has closed the connection."); + new AMQShortString(BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR); - @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") + @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-9-1/0-9/0-8 Connection") public AMQProtocolSessionMBean(AMQProtocolSession amqProtocolSession) throws NotCompliantMBeanException, OpenDataException { - super(ManagedConnection.class, ManagedConnection.TYPE); + super(amqProtocolSession.getRemoteAddress().toString()); _protocolSession = amqProtocolSession; - String remote = getRemoteAddress(); - _name = "anonymous".equals(remote) ? (remote + hashCode()) : remote; - init(); - } - - static - { - try - { - init(); - } - catch (JMException ex) - { - // This is not expected to ever occur. - throw new RuntimeException("Got JMException in static initializer.", ex); - } - } - - /** - * initialises the openmbean data types - */ - private static void init() throws OpenDataException - { - _channelType = - new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), - COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), _channelAttributeTypes); - _channelsType = new TabularType("Channels", "Channels", _channelType, TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); } public String getClientId() @@ -169,16 +124,6 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed return _protocolSession.getMaximumNumberOfChannels(); } - public void setMaximumNumberOfChannels(Long value) - { - _protocolSession.setMaximumNumberOfChannels(value); - } - - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } - /** * commits transactions for a transactional channel * @@ -321,25 +266,6 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed } } - @Override - public MBeanNotificationInfo[] getNotificationInfo() - { - String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; - String name = MonitorNotification.class.getName(); - String description = "Channel count has reached threshold value"; - MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); - - return new MBeanNotificationInfo[] { info1 }; - } - - public void notifyClients(String notificationMsg) - { - Notification n = - new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, - System.currentTimeMillis(), notificationMsg); - _broadcaster.sendNotification(n); - } - public void resetStatistics() throws Exception { _protocolSession.resetStatistics(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java index 48a8a1bf42..5d4b8c603b 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java @@ -100,6 +100,12 @@ public class ProtocolEngine_0_10 extends InputHandler implements ServerProtocol return _network.getLocalAddress(); } + public void received(final ByteBuffer buf) + { + super.received(buf); + _connection.receivedComplete(); + } + public long getReadBytes() { return _readBytes; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java index a9ddf9bb0c..dd4bd2bb1c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java @@ -20,6 +20,14 @@ */ package org.apache.qpid.server.protocol.v1_0; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQSecurityException; @@ -27,16 +35,28 @@ import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; import org.apache.qpid.amqp_1_0.transport.SendingLinkListener; -import org.apache.qpid.amqp_1_0.type.*; - -import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.Symbol; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.ExactSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Filter; +import org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter; +import org.apache.qpid.amqp_1_0.type.messaging.MatchingSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.NoLocalFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Released; import org.apache.qpid.amqp_1_0.type.messaging.Source; -import org.apache.qpid.amqp_1_0.type.transport.*; -import org.apache.qpid.AMQException; +import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transport.AmqpError; +import org.apache.qpid.amqp_1_0.type.transport.Detach; import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.exchange.ExchangeType; import org.apache.qpid.server.exchange.TopicExchange; import org.apache.qpid.server.filter.JMSSelectorMessageFilter; import org.apache.qpid.server.filter.SimpleFilterManager; @@ -47,9 +67,6 @@ import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryStateHandler { private VirtualHost _vhost; @@ -140,7 +157,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS try { queue = AMQQueueFactory.createAMQQueueImpl(UUID.randomUUID().toString(), false, null, true, - false, _vhost, Collections.EMPTY_MAP); + false, _vhost, Collections.EMPTY_MAP); Exchange exchange = ((ExchangeDestination) destination).getExchange(); String binding = ""; @@ -183,6 +200,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS catch (AMQInternalException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java index 4b189652d3..e08e7f3e39 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java @@ -365,6 +365,9 @@ public class Session_1_0 implements SessionEventListener catch (AMQSecurityException e) { e.printStackTrace(); //TODO. + } catch (AMQException e) + { + e.printStackTrace(); //TODO } return queue; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java index fc64527f3b..5dea91b6d4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java @@ -20,6 +20,13 @@ */ package org.apache.qpid.server.protocol.v1_0; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.apache.qpid.AMQException; import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; import org.apache.qpid.amqp_1_0.type.Binary; import org.apache.qpid.amqp_1_0.type.DeliveryState; @@ -31,24 +38,14 @@ import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; import org.apache.qpid.amqp_1_0.type.transport.SenderSettleMode; import org.apache.qpid.amqp_1_0.type.transport.Transfer; - -import org.apache.qpid.AMQException; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.ServerTransaction; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; - class Subscription_1_0 implements Subscription { private SendingLink_1_0 _link; @@ -171,6 +168,17 @@ class Subscription_1_0 implements Subscription getEndpoint().detach(); } + public void send(QueueEntry entry, boolean batch) throws AMQException + { + // TODO + send(entry); + } + + public void flushBatched() + { + // TODO + } + public void send(final QueueEntry queueEntry) throws AMQException { //TODO @@ -296,6 +304,11 @@ class Subscription_1_0 implements Subscription return !hasCredit; } + public boolean trySendLock() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + public void suspend() { if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) @@ -314,6 +327,11 @@ class Subscription_1_0 implements Subscription _stateChangeLock.unlock(); } + public void releaseQueueEntry(QueueEntry queueEntryImpl) + { + //To change body of implemented methods use File | Settings | File Templates. + } + public void onDequeue(final QueueEntry queueEntry) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java index 371ae0de50..2c04a626ff 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -20,71 +20,25 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionList; -import org.apache.qpid.server.virtualhost.VirtualHost; - import java.util.Map; +import org.apache.qpid.server.virtualhost.VirtualHost; -public class AMQPriorityQueue extends SimpleAMQQueue +public class AMQPriorityQueue extends OutOfOrderQueue { - protected AMQPriorityQueue(final AMQShortString name, - final boolean durable, - final AMQShortString owner, - final boolean autoDelete, - boolean exclusive, - final VirtualHost virtualHost, - int priorities, Map<String, Object> arguments) - { - super(name, durable, owner, autoDelete, exclusive, virtualHost,new PriorityQueueList.Factory(priorities), arguments); - } - - public AMQPriorityQueue(String queueName, - boolean durable, - String owner, - boolean autoDelete, - boolean exclusive, VirtualHost virtualHost, int priorities, Map<String,Object> arguments) + protected AMQPriorityQueue(final String name, + final boolean durable, + final String owner, + final boolean autoDelete, + boolean exclusive, + final VirtualHost virtualHost, + Map<String, Object> arguments, + int priorities) { - this(queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), - autoDelete, exclusive,virtualHost, priorities, arguments); + super(name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); } public int getPriorities() { return ((PriorityQueueList) _entries).getPriorities(); } - - @Override - protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) - { - // check that all subscriptions are not in advance of the entry - SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator(); - while(subIter.advance() && entry.isAvailable()) - { - final Subscription subscription = subIter.getNode().getSubscription(); - if(!subscription.isClosed()) - { - QueueContext context = (QueueContext) subscription.getQueueContext(); - if(context != null) - { - QueueEntry subnode = context._lastSeenEntry; - QueueEntry released = context._releasedEntry; - while(subnode != null && entry.compareTo(subnode) < 0 && entry.isAvailable() && (released == null || released.compareTo(entry) < 0)) - { - if(QueueContext._releasedUpdater.compareAndSet(context,released,entry)) - { - break; - } - else - { - subnode = context._lastSeenEntry; - released = context._releasedEntry; - } - } - } - } - - } - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 9140a13625..6dfdc5e8b4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -213,6 +213,8 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer void setAlternateExchange(Exchange exchange); + void setAlternateExchange(String exchangeName); + Map<String, Object> getArguments(); void checkCapacity(AMQChannel channel); @@ -272,4 +274,22 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer ManagedObject getManagedObject(); void setExclusive(boolean exclusive) throws AMQException; + + /** + * Gets the maximum delivery count. If a message on this queue + * is delivered more than maximumDeliveryCount, the message will be + * routed to the {@link #getAlternateExchange()} (if set), or otherwise + * discarded. 0 indicates that maximum deliver count should not be enforced. + * + * @return maximum delivery count + */ + int getMaximumDeliveryCount(); + + /** + * Sets the maximum delivery count. + * + * @param maximumDeliveryCount maximum delivery count + */ + public void setMaximumDeliveryCount(final int maximumDeliveryCount); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index 5fbad74978..14ca147982 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -20,22 +20,33 @@ */ package org.apache.qpid.server.queue; +import java.util.HashMap; +import java.util.Map; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.configuration.QueueConfiguration; - -import java.util.Map; -import java.util.HashMap; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; public class AMQQueueFactory { - public static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); + public static final String X_QPID_PRIORITIES = "x-qpid-priorities"; public static final String QPID_LVQ_KEY = "qpid.LVQ_key"; public static final String QPID_LAST_VALUE_QUEUE = "qpid.last_value_queue"; public static final String QPID_LAST_VALUE_QUEUE_KEY = "qpid.last_value_queue_key"; + public static final String QPID_QUEUE_SORT_KEY = "qpid.queue_sort_key"; + + public static final String DLQ_ROUTING_KEY = "dlq"; + public static final String X_QPID_DLQ_ENABLED = "x-qpid-dlq-enabled"; + public static final String X_QPID_MAXIMUM_DELIVERY_COUNT = "x-qpid-maximum-delivery-count"; + public static final String DEFAULT_DLQ_NAME_SUFFIX = "_DLQ"; private abstract static class QueueProperty { @@ -80,6 +91,24 @@ public class AMQQueueFactory } + private abstract static class QueueIntegerProperty extends QueueProperty + { + public QueueIntegerProperty(String argumentName) + { + super(argumentName); + } + + public void setPropertyValue(AMQQueue queue, Object value) + { + if(value instanceof Number) + { + setPropertyValue(queue, ((Number)value).intValue()); + } + + } + abstract void setPropertyValue(AMQQueue queue, int value); + } + private static final QueueProperty[] DECLAREABLE_PROPERTIES = { new QueueLongProperty("x-qpid-maximum-message-age") { @@ -122,8 +151,14 @@ public class AMQQueueFactory { queue.setFlowResumeCapacity(value); } + }, + new QueueIntegerProperty(X_QPID_MAXIMUM_DELIVERY_COUNT) + { + public void setPropertyValue(AMQQueue queue, int value) + { + queue.setMaximumDeliveryCount(value); + } } - }; @@ -149,17 +184,31 @@ public class AMQQueueFactory String owner, boolean autoDelete, boolean exclusive, - VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException + VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException, AMQException { + if (queueName == null) + { + throw new IllegalArgumentException("Queue name must not be null"); + } + // Access check if (!virtualHost.getSecurityManager().authoriseCreateQueue(autoDelete, durable, exclusive, null, null, new AMQShortString(queueName), owner)) { String description = "Permission denied: queue-name '" + queueName + "'"; throw new AMQSecurityException(description); } - + + QueueConfiguration queueConfiguration = virtualHost.getConfiguration().getQueueConfiguration(queueName); + boolean isDLQEnabled = isDLQEnabled(autoDelete, arguments, queueConfiguration); + if (isDLQEnabled) + { + validateDLNames(queueName); + } + int priorities = 1; String conflationKey = null; + String sortingKey = null; + if(arguments != null) { if(arguments.containsKey(QPID_LAST_VALUE_QUEUE) || arguments.containsKey(QPID_LAST_VALUE_QUEUE_KEY)) @@ -170,24 +219,32 @@ public class AMQQueueFactory conflationKey = QPID_LVQ_KEY; } } - else if(arguments.containsKey(X_QPID_PRIORITIES.toString())) + else if(arguments.containsKey(X_QPID_PRIORITIES)) { - Object prioritiesObj = arguments.get(X_QPID_PRIORITIES.toString()); + Object prioritiesObj = arguments.get(X_QPID_PRIORITIES); if(prioritiesObj instanceof Number) { priorities = ((Number)prioritiesObj).intValue(); } } + else if(arguments.containsKey(QPID_QUEUE_SORT_KEY)) + { + sortingKey = (String)arguments.get(QPID_QUEUE_SORT_KEY); + } } AMQQueue q; - if(conflationKey != null) + if(sortingKey != null) + { + q = new SortedQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); + } + else if(conflationKey != null) { q = new ConflationQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); } else if(priorities > 1) { - q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, priorities, arguments); + q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); } else { @@ -209,10 +266,63 @@ public class AMQQueueFactory } } - return q; + if(isDLQEnabled) + { + final String dlExchangeName = getDeadLetterExchangeName(queueName); + final String dlQueueName = getDeadLetterQueueName(queueName); - } + final ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + final ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + final QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + Exchange dlExchange = null; + synchronized(exchangeRegistry) + { + dlExchange = exchangeRegistry.getExchange(dlExchangeName); + + if(dlExchange == null) + { + dlExchange = exchangeFactory.createExchange(new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); + + exchangeRegistry.registerExchange(dlExchange); + + //enter the dle in the persistent store + virtualHost.getDurableConfigurationStore().createExchange(dlExchange); + } + } + + AMQQueue dlQueue = null; + + synchronized(queueRegistry) + { + dlQueue = queueRegistry.getQueue(dlQueueName); + + if(dlQueue == null) + { + //set args to disable DLQ'ing/MDC from the DLQ itself, preventing loops etc + final Map<String, Object> args = new HashMap<String, Object>(); + args.put(X_QPID_DLQ_ENABLED, false); + args.put(X_QPID_MAXIMUM_DELIVERY_COUNT, 0); + + dlQueue = createAMQQueueImpl(dlQueueName, true, owner, false, exclusive, virtualHost, args); + //enter the dlq in the persistent store + virtualHost.getDurableConfigurationStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); + } + } + + //ensure the queue is bound to the exchange + if(!dlExchange.isBound(DLQ_ROUTING_KEY, dlQueue)) + { + //actual routing key used does not matter due to use of fanout exchange, + //but we will make the key 'dlq' as it can be logged at creation. + virtualHost.getBindingFactory().addBinding(DLQ_ROUTING_KEY, dlQueue, dlExchange, null); + } + q.setAlternateExchange(dlExchange); + } + + return q; + } public static AMQQueue createAMQQueueImpl(QueueConfiguration config, VirtualHost host) throws AMQException { @@ -223,26 +333,30 @@ public class AMQQueueFactory boolean exclusive = config.getExclusive(); String owner = config.getOwner(); Map<String,Object> arguments = null; + if(config.isLVQ() || config.getLVQKey() != null) { - arguments = new HashMap<String,Object>(); arguments.put(QPID_LAST_VALUE_QUEUE, 1); arguments.put(QPID_LAST_VALUE_QUEUE_KEY, config.getLVQKey() == null ? QPID_LVQ_KEY : config.getLVQKey()); } - else + else if (config.getPriority() || config.getPriorities() > 0) + { + arguments = new HashMap<String,Object>(); + arguments.put(X_QPID_PRIORITIES, config.getPriorities() < 0 ? 10 : config.getPriorities()); + } + else if (config.getQueueSortKey() != null && !"".equals(config.getQueueSortKey())) + { + arguments = new HashMap<String,Object>(); + arguments.put(QPID_QUEUE_SORT_KEY, config.getQueueSortKey()); + } + if (!config.getAutoDelete() && config.isDeadLetterQueueEnabled()) { - boolean priority = config.getPriority(); - int priorities = config.getPriorities(); - if(priority || priorities > 0) + if (arguments == null) { arguments = new HashMap<String,Object>(); - if (priorities < 0) - { - priorities = 10; - } - arguments.put("x-qpid-priorities", priorities); } + arguments.put(X_QPID_DLQ_ENABLED, true); } if(config.isTopic()) @@ -259,4 +373,94 @@ public class AMQQueueFactory return q; } + + /** + * Validates DLQ and DLE names + * <p> + * DLQ name and DLQ exchange name need to be validated in order to keep + * integrity in cases when queue name passes validation check but DLQ name + * or DL exchange name fails to pass it. Otherwise, we might have situations + * when queue is created but DL exchange or/and DLQ creation fail. + * <p> + * + * @param name + * queue name + * @throws IllegalArgumentException + * thrown if length of queue name or exchange name exceed 255 + */ + protected static void validateDLNames(String name) + { + // check if DLQ name and DLQ exchange name do not exceed 255 + String exchangeName = getDeadLetterExchangeName(name); + if (exchangeName.length() > AMQShortString.MAX_LENGTH) + { + throw new IllegalArgumentException("DL exchange name '" + exchangeName + + "' length exceeds limit of " + AMQShortString.MAX_LENGTH + " characters for queue " + name); + } + String queueName = getDeadLetterQueueName(name); + if (queueName.length() > AMQShortString.MAX_LENGTH) + { + throw new IllegalArgumentException("DLQ queue name '" + queueName + "' length exceeds limit of " + + AMQShortString.MAX_LENGTH + " characters for queue " + name); + } + } + + /** + * Checks if DLQ is enabled for the queue. + * + * @param autoDelete + * queue auto-delete flag + * @param arguments + * queue arguments + * @param qConfig + * queue configuration + * @return true if DLQ enabled + */ + protected static boolean isDLQEnabled(boolean autoDelete, Map<String, Object> arguments, QueueConfiguration qConfig) + { + //feature is not to be enabled for temporary queues or when explicitly disabled by argument + if (!autoDelete) + { + boolean dlqArgumentPresent = arguments != null && arguments.containsKey(X_QPID_DLQ_ENABLED); + if (dlqArgumentPresent || qConfig.isDeadLetterQueueEnabled()) + { + boolean dlqEnabled = true; + if (dlqArgumentPresent) + { + Object argument = arguments.get(X_QPID_DLQ_ENABLED); + dlqEnabled = argument instanceof Boolean && ((Boolean)argument).booleanValue(); + } + return dlqEnabled; + } + } + return false; + } + + /** + * Generates a dead letter queue name for a given queue name + * + * @param name + * queue name + * @return DLQ name + */ + protected static String getDeadLetterQueueName(String name) + { + ServerConfiguration serverConfig = ApplicationRegistry.getInstance().getConfiguration(); + String dlQueueName = name + serverConfig.getDeadLetterQueueSuffix(); + return dlQueueName; + } + + /** + * Generates a dead letter exchange name for a given queue name + * + * @param name + * queue name + * @return DL exchange name + */ + protected static String getDeadLetterExchangeName(String name) + { + ServerConfiguration serverConfig = ApplicationRegistry.getInstance().getConfiguration(); + String dlExchangeName = name + serverConfig.getDeadLetterExchangeSuffix(); + return dlExchangeName; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index c8eb118b11..b4765d6227 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -28,7 +28,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; @@ -63,23 +63,25 @@ import java.util.*; /** * AMQQueueMBean is the management bean for an {@link AMQQueue}. * - * <p/><tablse id="crc"><caption>CRC Caption</caption> + * <p/><table id="crc"><caption>CRC Caption</caption> * <tr><th> Responsibilities <th> Collaborations * </table> */ @MBeanDescription("Management Interface for AMQQueue") public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, QueueNotificationListener { + /** Used for debugging purposes. */ private static final Logger _logger = Logger.getLogger(AMQQueueMBean.class); - private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm:ss.SSS z"); + /** Date/time format used for message expiration and message timestamp formatting */ + public static final String JMSTIMESTAMP_DATETIME_FORMAT = "MM-dd-yy HH:mm:ss.SSS z"; - private AMQQueue _queue = null; - private String _queueName = null; + private final AMQQueue _queue; + private final String _queueName; // OpenMBean data types for viewMessages method - private static OpenType[] _msgAttributeTypes = new OpenType[5]; // AMQ message attribute types. + private static OpenType[] _msgAttributeTypes = new OpenType[6]; // AMQ message attribute types. private static CompositeType _messageDataType = null; // Composite type for representing AMQ Message data. private static TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list. @@ -138,6 +140,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que _msgAttributeTypes[2] = SimpleType.LONG; // For size _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered _msgAttributeTypes[4] = SimpleType.LONG; // For queue position + _msgAttributeTypes[5] = SimpleType.INTEGER; // For delivery count _messageDataType = new CompositeType("Message", "AMQ Message", VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]), @@ -176,6 +179,11 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que return _queue.getMessageCount(); } + public Integer getMaximumDeliveryCount() + { + return _queue.getMaximumDeliveryCount(); + } + public Long getMaximumMessageSize() { return _queue.getMaximumMessageSize(); @@ -294,6 +302,18 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que } } + public void setAlternateExchange(String exchangeName) + { + _queue.setAlternateExchange(exchangeName); + } + + public String getAlternateExchange() + { + Exchange exchange = _queue.getAlternateExchange(); + String name = exchange == null ? null : exchange.getName(); + return name == null ? null : name; + } + /** * Checks if there is any notification to be send to the listeners */ @@ -471,7 +491,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que ContentHeaderBody headerBody = msg.getContentHeaderBody(); // Create header attributes list headerAttributes = getMessageHeaderProperties(headerBody); - itemValues = new Object[]{msg.getMessageId(), headerAttributes, headerBody.bodySize, queueEntry.isRedelivered(), position}; + itemValues = new Object[]{msg.getMessageId(), headerAttributes, headerBody.bodySize, queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } else if(serverMsg instanceof MessageTransferMessage) { @@ -480,13 +500,13 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que // Create header attributes list headerAttributes = getMessageTransferMessageHeaderProps(msg); - itemValues = new Object[]{msg.getMessageNumber(), headerAttributes, msg.getSize(), queueEntry.isRedelivered(), position}; + itemValues = new Object[]{msg.getMessageNumber(), headerAttributes, msg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } else { //unknown message headerAttributes = new String[]{"N/A"}; - itemValues = new Object[]{serverMsg.getMessageNumber(), headerAttributes, serverMsg.getSize(), queueEntry.isRedelivered(), position}; + itemValues = new Object[]{serverMsg.getMessageNumber(), headerAttributes, serverMsg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } CompositeData messageData = new CompositeDataSupport(_messageDataType, @@ -523,13 +543,11 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que list.add("JMSPriority = " + headerProperties.getPriority()); list.add("JMSType = " + headerProperties.getType()); - long longDate = headerProperties.getExpiration(); - String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSExpiration = " + strDate); + final long expirationDate = headerProperties.getExpiration(); + final long timestampDate = headerProperties.getTimestamp(); - longDate = headerProperties.getTimestamp(); - strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSTimestamp = " + strDate); + addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, + timestampDate); return list.toArray(new String[list.size()]); } @@ -561,17 +579,32 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que list.add("JMSPriority = " + header.getPriority()); list.add("JMSType = " + header.getType()); - long longDate = header.getExpiration(); - String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSExpiration = " + strDate); - - longDate = header.getTimestamp(); - strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSTimestamp = " + strDate); + final long expirationDate = header.getExpiration(); + final long timestampDate = header.getTimestamp(); + addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, timestampDate); return list.toArray(new String[list.size()]); } + private void addStringifiedJMSTimestamoAndJMSExpiration(final List<String> list, + final long expirationDate, final long timestampDate) + { + final SimpleDateFormat dateFormat; + if (expirationDate != 0 || timestampDate != 0) + { + dateFormat = new SimpleDateFormat(JMSTIMESTAMP_DATETIME_FORMAT); + } + else + { + dateFormat = null; + } + + final String formattedExpirationDate = (expirationDate != 0) ? dateFormat.format(new Date(expirationDate)) : null; + final String formattedTimestampDate = (timestampDate != 0) ? dateFormat.format(new Date(timestampDate)) : null; + list.add("JMSExpiration = " + formattedExpirationDate); + list.add("JMSTimestamp = " + formattedTimestampDate); + } + /** * @see ManagedQueue#moveMessages * @param fromMessageId diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java index 2c1883e763..c4762c98c9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java @@ -54,7 +54,7 @@ public class ConflationQueueList extends SimpleQueueEntryList @Override - public QueueEntry add(final ServerMessage message) + public ConflationQueueEntry add(final ServerMessage message) { ConflationQueueEntry entry = (ConflationQueueEntry) (super.add(message)); AtomicReference<QueueEntry> latestValueReference = null; @@ -117,7 +117,7 @@ public class ConflationQueueList extends SimpleQueueEntryList } } - private final class ConflationQueueEntry extends QueueEntryImpl + private final class ConflationQueueEntry extends SimpleQueueEntryImpl { @@ -158,7 +158,7 @@ public class ConflationQueueList extends SimpleQueueEntryList _conflationKey = conflationKey; } - public QueueEntryList createQueueEntryList(AMQQueue queue) + public ConflationQueueList createQueueEntryList(AMQQueue queue) { return new ConflationQueueList(queue, _conflationKey); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java index 77da08d8c4..26112d9f53 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java @@ -24,7 +24,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.AMQMessageHeader; -class InboundMessageAdapter implements InboundMessage +public class InboundMessageAdapter implements InboundMessage { private QueueEntry _entry; @@ -33,7 +33,7 @@ class InboundMessageAdapter implements InboundMessage { } - InboundMessageAdapter(QueueEntry entry) + public InboundMessageAdapter(QueueEntry entry) { _entry = entry; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java new file mode 100644 index 0000000000..b16d1eb8e3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java @@ -0,0 +1,53 @@ +package org.apache.qpid.server.queue; + +import java.util.Map; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public abstract class OutOfOrderQueue extends SimpleAMQQueue +{ + protected OutOfOrderQueue(String name, boolean durable, String owner, + boolean autoDelete, boolean exclusive, VirtualHost virtualHost, + QueueEntryListFactory entryListFactory, Map<String, Object> arguments) + { + super(name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); + } + + @Override + protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + { + // check that all subscriptions are not in advance of the entry + SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator(); + while(subIter.advance() && !entry.isAcquired()) + { + final Subscription subscription = subIter.getNode().getSubscription(); + if(!subscription.isClosed()) + { + QueueContext context = (QueueContext) subscription.getQueueContext(); + if(context != null) + { + QueueEntry subnode = context._lastSeenEntry; + QueueEntry released = context._releasedEntry; + + while(subnode != null && entry.compareTo(subnode) < 0 && !entry.isAcquired() + && (released == null || released.compareTo(entry) > 0)) + { + if(QueueContext._releasedUpdater.compareAndSet(context,released,entry)) + { + break; + } + else + { + subnode = context._lastSeenEntry; + released = context._releasedEntry; + } + + } + } + } + + } + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java index 0c6b84d2b6..79d3ab5bd0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -20,21 +20,19 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.framing.CommonContentHeaderProperties; -import org.apache.qpid.AMQException; import org.apache.qpid.server.message.ServerMessage; -public class PriorityQueueList implements QueueEntryList +public class PriorityQueueList implements QueueEntryList<SimpleQueueEntryImpl> { private final AMQQueue _queue; - private final QueueEntryList[] _priorityLists; + private final SimpleQueueEntryList[] _priorityLists; private final int _priorities; private final int _priorityOffset; public PriorityQueueList(AMQQueue queue, int priorities) { _queue = queue; - _priorityLists = new QueueEntryList[priorities]; + _priorityLists = new SimpleQueueEntryList[priorities]; _priorities = priorities; _priorityOffset = 5-((priorities + 1)/2); for(int i = 0; i < priorities; i++) @@ -53,7 +51,7 @@ public class PriorityQueueList implements QueueEntryList return _queue; } - public QueueEntry add(ServerMessage message) + public SimpleQueueEntryImpl add(ServerMessage message) { int index = message.getMessageHeader().getPriority() - _priorityOffset; if(index >= _priorities) @@ -68,31 +66,30 @@ public class PriorityQueueList implements QueueEntryList } - public QueueEntry next(QueueEntry node) + public SimpleQueueEntryImpl next(SimpleQueueEntryImpl node) { - QueueEntryImpl nodeImpl = (QueueEntryImpl)node; - QueueEntry next = nodeImpl.getNext(); + SimpleQueueEntryImpl next = node.getNextValidEntry(); if(next == null) { - QueueEntryList nodeEntryList = nodeImpl.getQueueEntryList(); + final QueueEntryList<?> nodeEntryList = node.getQueueEntryList(); int index; for(index = _priorityLists.length-1; _priorityLists[index] != nodeEntryList; index--); while(next == null && index != 0) { index--; - next = ((QueueEntryImpl)_priorityLists[index].getHead()).getNext(); + next = _priorityLists[index].getHead().getNextValidEntry(); } } return next; } - private final class PriorityQueueEntryListIterator implements QueueEntryIterator + private final class PriorityQueueEntryListIterator implements QueueEntryIterator<SimpleQueueEntryImpl> { - private final QueueEntryIterator[] _iterators = new QueueEntryIterator[ _priorityLists.length ]; - private QueueEntry _lastNode; + private final SimpleQueueEntryList.QueueEntryIteratorImpl[] _iterators = new SimpleQueueEntryList.QueueEntryIteratorImpl[ _priorityLists.length ]; + private SimpleQueueEntryImpl _lastNode; PriorityQueueEntryListIterator() { @@ -116,7 +113,7 @@ public class PriorityQueueList implements QueueEntryList return true; } - public QueueEntry getNode() + public SimpleQueueEntryImpl getNode() { return _lastNode; } @@ -135,16 +132,21 @@ public class PriorityQueueList implements QueueEntryList } } - public QueueEntryIterator iterator() + public PriorityQueueEntryListIterator iterator() { return new PriorityQueueEntryListIterator(); } - public QueueEntry getHead() + public SimpleQueueEntryImpl getHead() { return _priorityLists[_priorities-1].getHead(); } + public void entryDeleted(final SimpleQueueEntryImpl queueEntry) + { + + } + static class Factory implements QueueEntryListFactory { private final int _priorities; @@ -154,7 +156,7 @@ public class PriorityQueueList implements QueueEntryList _priorities = priorities; } - public QueueEntryList createQueueEntryList(AMQQueue queue) + public PriorityQueueList createQueueEntryList(AMQQueue queue) { return new PriorityQueueList(queue, _priorities); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java index be29245901..37fad54c07 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -1,5 +1,7 @@ package org.apache.qpid.server.queue; +import java.util.Collection; + import org.apache.qpid.AMQException; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.message.ServerMessage; @@ -214,6 +216,10 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable boolean isQueueDeleted(); + QueueEntry getNextNode(); + + QueueEntry getNextValidEntry(); + void addStateChangeListener(StateChangeListener listener); boolean removeStateChangeListener(StateChangeListener listener); @@ -230,4 +236,16 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable * @return true if entry is either DEQUED or DELETED state */ boolean isDispensed(); + + /** + * Number of times this queue entry has been delivered. + * + * @return delivery count + */ + int getDeliveryCount(); + + void incrementDeliveryCount(); + + void decrementDeliveryCount(); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 5b57e40a82..f1e50427b1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -20,8 +20,14 @@ */ package org.apache.qpid.server.queue; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.message.AMQMessageHeader; @@ -31,23 +37,11 @@ import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - - -public class QueueEntryImpl implements QueueEntry +public abstract class QueueEntryImpl implements QueueEntry { - - /** - * Used for debugging purposes. - */ private static final Logger _log = Logger.getLogger(QueueEntryImpl.class); - private final SimpleQueueEntryList _queueEntryList; + private final QueueEntryList _queueEntryList; private MessageReference _message; @@ -80,22 +74,26 @@ public class QueueEntryImpl implements QueueEntry private volatile long _entryId; - volatile QueueEntryImpl _next; - private static final int DELIVERED_TO_CONSUMER = 1; private static final int REDELIVERED = 2; private volatile int _deliveryState; + /** Number of times this message has been delivered */ + private volatile int _deliveryCount = 0; + private static final AtomicIntegerFieldUpdater<QueueEntryImpl> _deliveryCountUpdater = AtomicIntegerFieldUpdater + .newUpdater(QueueEntryImpl.class, "_deliveryCount"); + - QueueEntryImpl(SimpleQueueEntryList queueEntryList) + + public QueueEntryImpl(QueueEntryList<?> queueEntryList) { this(queueEntryList,null,Long.MIN_VALUE); _state = DELETED_STATE; } - public QueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message, final long entryId) + public QueueEntryImpl(QueueEntryList<?> queueEntryList, ServerMessage message, final long entryId) { _queueEntryList = queueEntryList; @@ -104,7 +102,7 @@ public class QueueEntryImpl implements QueueEntry _entryIdUpdater.set(this, entryId); } - public QueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message) + public QueueEntryImpl(QueueEntryList<?> queueEntryList, ServerMessage message) { _queueEntryList = queueEntryList; _message = message == null ? null : message.newReference(); @@ -233,8 +231,13 @@ public class QueueEntryImpl implements QueueEntry if(state instanceof SubscriptionAcquiredState) { getQueue().decrementUnackedMsgCount(); + Subscription subscription = ((SubscriptionAcquiredState)state).getSubscription(); + if (subscription != null) + { + subscription.releaseQueueEntry(this); + } } - + if(!getQueue().isDeleted()) { getQueue().requeue(this); @@ -311,16 +314,15 @@ public class QueueEntryImpl implements QueueEntry public Subscription getDeliveredSubscription() { - EntryState state = _state; - if (state instanceof SubscriptionAcquiredState) - { - return ((SubscriptionAcquiredState) state).getSubscription(); - } - else - { - return null; - } - + EntryState state = _state; + if (state instanceof SubscriptionAcquiredState) + { + return ((SubscriptionAcquiredState) state).getSubscription(); + } + else + { + return null; + } } public void reject() @@ -409,50 +411,51 @@ public class QueueEntryImpl implements QueueEntry public void routeToAlternate() { final AMQQueue currentQueue = getQueue(); - Exchange alternateExchange = currentQueue.getAlternateExchange(); + Exchange alternateExchange = currentQueue.getAlternateExchange(); - if(alternateExchange != null) + if (alternateExchange != null) + { + final List<? extends BaseQueue> rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); + final ServerMessage message = getMessage(); + if (rerouteQueues != null && rerouteQueues.size() != 0) { - final List<? extends BaseQueue> rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); - final ServerMessage message = getMessage(); - if(rerouteQueues != null && rerouteQueues.size() != 0) - { - ServerTransaction txn = new AutoCommitTransaction(getQueue().getVirtualHost().getTransactionLog()); - txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() { - public void postCommit() + ServerTransaction txn = new AutoCommitTransaction(getQueue().getVirtualHost().getTransactionLog()); + + txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() + { + public void postCommit() + { + try { - try + for (BaseQueue queue : rerouteQueues) { - for(BaseQueue queue : rerouteQueues) - { - queue.enqueue(message); - } - } - catch (AMQException e) - { - throw new RuntimeException(e); + queue.enqueue(message); } } - - public void onRollback() + catch (AMQException e) { - + throw new RuntimeException(e); } - }); - txn.dequeue(currentQueue,message, - new ServerTransaction.Action() - { - public void postCommit() - { - discard(); - } - - public void onRollback() - { - - } - }); + } + + public void onRollback() + { + + } + }); + txn.dequeue(currentQueue, message, new ServerTransaction.Action() + { + public void postCommit() + { + discard(); + } + + public void onRollback() + { + + } + }); } } } @@ -492,33 +495,6 @@ public class QueueEntryImpl implements QueueEntry return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0; } - public QueueEntryImpl getNext() - { - - QueueEntryImpl next = nextNode(); - while(next != null && next.isDispensed() ) - { - - final QueueEntryImpl newNext = next.nextNode(); - if(newNext != null) - { - SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext); - next = nextNode(); - } - else - { - next = null; - } - - } - return next; - } - - QueueEntryImpl nextNode() - { - return _next; - } - public boolean isDeleted() { return _state == DELETED_STATE; @@ -530,7 +506,7 @@ public class QueueEntryImpl implements QueueEntry if(state != DELETED_STATE && _stateUpdater.compareAndSet(this,state,DELETED_STATE)) { - _queueEntryList.advanceHead(); + _queueEntryList.entryDeleted(this); return true; } else @@ -554,4 +530,19 @@ public class QueueEntryImpl implements QueueEntry return _state.isDispensed(); } + public int getDeliveryCount() + { + return _deliveryCount; + } + + public void incrementDeliveryCount() + { + _deliveryCountUpdater.incrementAndGet(this); + } + + public void decrementDeliveryCount() + { + _deliveryCountUpdater.decrementAndGet(this); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java index c5c115a2d1..73ebb0f300 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java @@ -20,11 +20,11 @@ */ package org.apache.qpid.server.queue; -public interface QueueEntryIterator +public interface QueueEntryIterator<QE extends QueueEntry> { boolean atTail(); - QueueEntry getNode(); + QE getNode(); boolean advance(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java index b4042ce02c..77c4b912e0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -22,15 +22,17 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.message.ServerMessage; -public interface QueueEntryList +public interface QueueEntryList<Q extends QueueEntry> { AMQQueue getQueue(); - QueueEntry add(ServerMessage message); + Q add(ServerMessage message); - QueueEntry next(QueueEntry node); + Q next(Q node); - QueueEntryIterator iterator(); + QueueEntryIterator<Q> iterator(); - QueueEntry getHead(); + Q getHead(); + + void entryDeleted(Q queueEntry); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java index 7e1d57e205..5270f9f740 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java @@ -27,6 +27,11 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.queue.QueueRunner; import org.apache.qpid.server.queue.SimpleAMQQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + /** * QueueRunners are Runnables used to process a queue when requiring * asynchronous message delivery to subscriptions, which is necessary @@ -37,33 +42,64 @@ public class QueueRunner implements ReadWriteRunnable { private static final Logger _logger = Logger.getLogger(QueueRunner.class); - private final String _name; private final SimpleAMQQueue _queue; - public QueueRunner(SimpleAMQQueue queue, long count) + private static int IDLE = 0; + private static int SCHEDULED = 1; + private static int RUNNING = 2; + + + private final AtomicInteger _scheduled = new AtomicInteger(IDLE); + + private static final long ITERATIONS = SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + private final AtomicBoolean _stateChange = new AtomicBoolean(); + + private final AtomicLong _lastRunAgain = new AtomicLong(); + private final AtomicLong _lastRunTime = new AtomicLong(); + + private long _runs; + private long _continues; + + public QueueRunner(SimpleAMQQueue queue) { _queue = queue; - _name = "QueueRunner-" + count + "-" + queue.getLogActor(); } + private int trouble = 0; + public void run() { - String originalName = Thread.currentThread().getName(); - try + if(_scheduled.compareAndSet(SCHEDULED,RUNNING)) { - Thread.currentThread().setName(_name); - CurrentActor.set(_queue.getLogActor()); + long runAgain = Long.MIN_VALUE; + _stateChange.set(false); + try + { + CurrentActor.set(_queue.getLogActor()); + + runAgain = _queue.processQueue(this); + } + catch (AMQException e) + { + _logger.error("Exception during asynchronous delivery by " + toString(), e); + } + finally + { + CurrentActor.remove(); + } + _scheduled.compareAndSet(RUNNING, IDLE); + long stateChangeCount = _queue.getStateChangeCount(); + _lastRunAgain.set(runAgain); + _lastRunTime.set(System.nanoTime()); + if(runAgain == 0L || runAgain != stateChangeCount || _stateChange.compareAndSet(true,false)) + { + _continues++; + if(_scheduled.compareAndSet(IDLE, SCHEDULED)) + { + _queue.execute(this); + } + } - _queue.processQueue(this); - } - catch (AMQException e) - { - _logger.error("Exception during asynchronous delivery by " + _name, e); - } - finally - { - CurrentActor.remove(); - Thread.currentThread().setName(originalName); } } @@ -79,6 +115,21 @@ public class QueueRunner implements ReadWriteRunnable public String toString() { - return _name; + return "QueueRunner-" + _queue.getLogActor(); } -}
\ No newline at end of file + + public void execute(Executor executor) + { + _stateChange.set(true); + if(_scheduled.compareAndSet(IDLE, SCHEDULED)) + { + executor.execute(this); + } + } + + public boolean isIdle() + { + return _scheduled.get() == IDLE; + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 7effb1c0f8..08dab4e5fc 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -155,11 +155,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class); - static final int MAX_ASYNC_DELIVERIES = 10; + static final int MAX_ASYNC_DELIVERIES = 80; private final AtomicLong _stateChangeCount = new AtomicLong(Long.MIN_VALUE); - private AtomicReference<Runnable> _asynchronousRunner = new AtomicReference<Runnable>(null); + private final Executor _asyncDelivery; private AtomicInteger _deliveredMessages = new AtomicInteger(); private AtomicBoolean _stopped = new AtomicBoolean(false); @@ -188,6 +188,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private ConfigurationPlugin _queueConfiguration; private final boolean _isTopic; + /** the maximum delivery count for each message on this queue or 0 if maximum delivery count is not to be enforced. */ + private int _maximumDeliveryCount = ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount(); protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String,Object> arguments) { @@ -358,6 +360,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _alternateExchange = exchange; } + public void setAlternateExchange(String exchangeName) + { + if(exchangeName == null || exchangeName.equals("")) + { + _alternateExchange = null; + return; + } + + Exchange exchange = getVirtualHost().getExchangeRegistry().getExchange(new AMQShortString(exchangeName)); + if (exchange == null) + { + throw new RuntimeException("Exchange '" + exchangeName + "' is not registered with the VirtualHost."); + } + setAlternateExchange(exchange); + } + public Map<String, Object> getArguments() { return _arguments; @@ -528,13 +546,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener //Reconfigure the queue for to reflect this new binding. ConfigurationPlugin config = getVirtualHost().getConfiguration().getQueueConfiguration(this); - if (_logger.isDebugEnabled()) - { - _logger.debug("Reconfiguring queue(" + this + ") with config:" + config + " was "+ _queueConfiguration); - } - if (config != null) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Reconfiguring queue(" + this + ") with config:" + config + " was "+ _queueConfiguration); + } // Reconfigure with new config. configure(config); } @@ -575,40 +592,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException { + incrementTxnEnqueueStats(message); + incrementQueueCount(); + incrementQueueSize(message); + _totalMessagesReceived.incrementAndGet(); - Subscription exclusiveSub = _exclusiveSubscriber; + final Subscription exclusiveSub = _exclusiveSubscriber; if(!_isTopic || _subscriptionList.size()!=0) { - incrementTxnEnqueueStats(message); - incrementQueueCount(); - incrementQueueSize(message); - - QueueEntry entry; + QueueEntry entry = _entries.add(message); - if (exclusiveSub != null) + if(action != null || (exclusiveSub == null && _queueRunner.isIdle())) { - exclusiveSub.getSendLock(); - - try - { - entry = _entries.add(message); - - deliverToSubscription(exclusiveSub, entry); - } - finally - { - exclusiveSub.releaseSendLock(); - } - } - else - { - entry = _entries.add(message); /* - iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message - */ SubscriptionList.SubscriptionNode node = _subscriptionList.getMarkedNode(); SubscriptionList.SubscriptionNode nextNode = node.findNext(); @@ -654,12 +653,20 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + if (entry.isAvailable()) { checkSubscriptionsNotAheadOfDelivery(entry); - deliverAsync(); + if (exclusiveSub != null) + { + deliverAsync(exclusiveSub); + } + else + { + deliverAsync(); + } } if(_managedObject != null) @@ -678,30 +685,32 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener throws AMQException { - sub.getSendLock(); - try + if(sub.trySendLock()) { - if (subscriptionReadyAndHasInterest(sub, entry) - && !sub.isSuspended()) + try { - if (!sub.wouldSuspend(entry)) + if (subscriptionReadyAndHasInterest(sub, entry) + && !sub.isSuspended()) { - if (sub.acquires() && !entry.acquire(sub)) + if (!sub.wouldSuspend(entry)) { - // restore credit here that would have been taken away by wouldSuspend since we didn't manage - // to acquire the entry for this subscription - sub.onDequeue(entry); - } - else - { - deliverMessage(sub, entry); + if (sub.acquires() && !entry.acquire(sub)) + { + // restore credit here that would have been taken away by wouldSuspend since we didn't manage + // to acquire the entry for this subscription + sub.restoreCredit(entry); + } + else + { + deliverMessage(sub, entry, false); + } } } } - } - finally - { - sub.releaseSendLock(); + finally + { + sub.releaseSendLock(); + } } } @@ -745,7 +754,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _byteTxnDequeues.addAndGet(entry.getSize()); } - private void deliverMessage(final Subscription sub, final QueueEntry entry) + private void deliverMessage(final Subscription sub, final QueueEntry entry, boolean batch) throws AMQException { setLastSeenEntry(sub, entry); @@ -753,7 +762,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _deliveredMessages.incrementAndGet(); incrementUnackedMsgCount(); - sub.send(entry); + sub.send(entry, batch); if(_isTopic) { @@ -893,7 +902,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { if (!subscription.isClosed()) { - deliverMessage(subscription, entry); + deliverMessage(subscription, entry, false); return true; } else @@ -1035,6 +1044,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _exclusiveSubscriber = exclusiveSubscriber; } + long getStateChangeCount() + { + return _stateChangeCount.get(); + } + + public static interface QueueEntryFilter { public boolean accept(QueueEntry entry); @@ -1335,7 +1350,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener QueueEntryIterator queueListIterator = _entries.iterator(); long count = 0; - ServerTransaction txn = new LocalTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); while (queueListIterator.advance()) { @@ -1358,7 +1373,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private void dequeueEntry(final QueueEntry node) { - ServerTransaction txn = new AutoCommitTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new AutoCommitTransaction(getVirtualHost().getMessageStore()); dequeueEntry(node, txn); } @@ -1435,7 +1450,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } }); - ServerTransaction txn = new LocalTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); if(_alternateExchange != null) { @@ -1604,26 +1619,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + private QueueRunner _queueRunner = new QueueRunner(this); public void deliverAsync() { - QueueRunner runner = new QueueRunner(this, _stateChangeCount.incrementAndGet()); + _stateChangeCount.incrementAndGet(); + + _queueRunner.execute(_asyncDelivery); - if (_asynchronousRunner.compareAndSet(null, runner)) - { - _asyncDelivery.execute(runner); - } } public void deliverAsync(Subscription sub) { - SubFlushRunner flusher = (SubFlushRunner) sub.get(SUB_FLUSH_RUNNER); - if(flusher == null) + //_stateChangeCount.incrementAndGet(); + if(_exclusiveSubscriber == null) { - flusher = new SubFlushRunner(sub); - sub.set(SUB_FLUSH_RUNNER, flusher); + deliverAsync(); } - _asyncDelivery.execute(flusher); + else + { + SubFlushRunner flusher = (SubFlushRunner) sub.get(SUB_FLUSH_RUNNER); + if(flusher == null) + { + flusher = new SubFlushRunner(sub); + sub.set(SUB_FLUSH_RUNNER, flusher); + } + flusher.execute(_asyncDelivery); + } + } public void flushSubscription(Subscription sub) throws AMQException @@ -1639,25 +1662,49 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public boolean flushSubscription(Subscription sub, long iterations) throws AMQException { boolean atTail = false; + final boolean keepSendLockHeld = iterations <= SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + boolean queueEmpty = false; - while (!sub.isSuspended() && !atTail && iterations != 0) + try { - boolean queueEmpty = false; - try + + if(keepSendLockHeld) { sub.getSendLock(); - atTail = attemptDelivery(sub); - if (atTail && getNextAvailableEntry(sub) == null) + } + + while (!sub.isSuspended() && !atTail && iterations != 0) + { + try { - queueEmpty = true; + if(!keepSendLockHeld) + { + sub.getSendLock(); + } + + atTail = attemptDelivery(sub, true); + if (atTail && getNextAvailableEntry(sub) == null) + { + queueEmpty = true; + } + else if (!atTail) + { + iterations--; + } } - else if (!atTail) + finally { - iterations--; + if(!keepSendLockHeld) + { + sub.releaseSendLock(); + } } } - finally + } + finally + { + if(keepSendLockHeld) { sub.releaseSendLock(); } @@ -1665,8 +1712,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { sub.queueEmpty(); } + sub.flushBatched(); + } + // if there's (potentially) more than one subscription the others will potentially not have been advanced to the // next entry they are interested in yet. This would lead to holding on to references to expired messages, etc // which would give us memory "leak". @@ -1684,11 +1734,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener * * Looks up the next node for the subscription and attempts to deliver it. * + * * @param sub + * @param batch * @return true if we have completed all possible deliveries for this sub. * @throws AMQException */ - private boolean attemptDelivery(Subscription sub) throws AMQException + private boolean attemptDelivery(Subscription sub, boolean batch) throws AMQException { boolean atTail = false; @@ -1706,11 +1758,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { if (sub.acquires() && !node.acquire(sub)) { - sub.onDequeue(node); + // restore credit here that would have been taken away by wouldSuspend since we didn't manage + // to acquire the entry for this subscription + sub.restoreCredit(node); } else { - deliverMessage(sub, node); + deliverMessage(sub, node, batch); } } @@ -1814,23 +1868,26 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener * @param runner the Runner to schedule * @throws AMQException */ - public void processQueue(QueueRunner runner) throws AMQException + public long processQueue(QueueRunner runner) throws AMQException { - long stateChangeCount; + long stateChangeCount = Long.MIN_VALUE; long previousStateChangeCount = Long.MIN_VALUE; + long rVal = Long.MIN_VALUE; boolean deliveryIncomplete = true; boolean lastLoop = false; int iterations = MAX_ASYNC_DELIVERIES; - _asynchronousRunner.compareAndSet(runner, null); + final int numSubs = _subscriptionList.size(); + + final int perSub = Math.max(iterations / Math.max(numSubs,1), 1); // For every message enqueue/requeue the we fire deliveryAsync() which // increases _stateChangeCount. If _sCC changes whilst we are in our loop // (detected by setting previousStateChangeCount to stateChangeCount in the loop body) // then we will continue to run for a maximum of iterations. // So whilst delivery/rejection is going on a processQueue thread will be running - while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete) && _asynchronousRunner.compareAndSet(null, runner)) + while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete)) { // we want to have one extra loop after every subscription has reached the point where it cannot move // further, just in case the advance of one subscription in the last loop allows a different subscription to @@ -1841,6 +1898,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener //further asynchronous delivery is required since the //previous loop. keep going if iteration slicing allows. lastLoop = false; + rVal = stateChangeCount; } previousStateChangeCount = stateChangeCount; @@ -1853,30 +1911,43 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { Subscription sub = subscriptionIter.getNode().getSubscription(); sub.getSendLock(); - try - { - //attempt delivery. returns true if no further delivery currently possible to this sub - subscriptionDone = attemptDelivery(sub); - if (subscriptionDone) + + try { - if(lastLoop) + for(int i = 0 ; i < perSub; i++) { - sub.queueEmpty(); + //attempt delivery. returns true if no further delivery currently possible to this sub + subscriptionDone = attemptDelivery(sub, true); + if (subscriptionDone) + { + sub.flushBatched(); + //close autoClose subscriptions if we are not currently intent on continuing + if (lastLoop && !sub.isSuspended() ) + { + sub.queueEmpty(); + } + break; + } + else + { + //this subscription can accept additional deliveries, so we must + //keep going after this (if iteration slicing allows it) + allSubscriptionsDone = false; + lastLoop = false; + if(--iterations == 0) + { + sub.flushBatched(); + break; + } + } } + + sub.flushBatched(); } - else + finally { - //this subscription can accept additional deliveries, so we must - //keep going after this (if iteration slicing allows it) - allSubscriptionsDone = false; - lastLoop = false; - iterations--; + sub.releaseSendLock(); } - } - finally - { - sub.releaseSendLock(); - } } if(allSubscriptionsDone && lastLoop) @@ -1902,24 +1973,24 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener deliveryIncomplete = true; } - _asynchronousRunner.set(null); } // If iterations == 0 then the limiting factor was the time-slicing rather than available messages or credit // therefore we should schedule this runner again (unless someone beats us to it :-) ). - if (iterations == 0 && _asynchronousRunner.compareAndSet(null, runner)) + if (iterations == 0) { if (_logger.isDebugEnabled()) { _logger.debug("Rescheduling runner:" + runner); } - _asyncDelivery.execute(runner); + return 0L; } + return rVal; + } public void checkMessageStatus() throws AMQException { - QueueEntryIterator queueListIterator = _entries.iterator(); while (queueListIterator.advance()) @@ -2150,6 +2221,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener setMaximumMessageSize(((QueueConfiguration)config).getMaximumMessageSize()); setMaximumMessageCount(((QueueConfiguration)config).getMaximumMessageCount()); setMinimumAlertRepeatGap(((QueueConfiguration)config).getMinimumAlertRepeatGap()); + setMaximumDeliveryCount(((QueueConfiguration)config).getMaxDeliveryCount()); _capacity = ((QueueConfiguration)config).getCapacity(); _flowResumeCapacity = ((QueueConfiguration)config).getFlowResumeCapacity(); } @@ -2271,4 +2343,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { return _logActor; } + + public int getMaximumDeliveryCount() + { + return _maximumDeliveryCount; + } + + public void setMaximumDeliveryCount(final int maximumDeliveryCount) + { + _maximumDeliveryCount = maximumDeliveryCount; + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java new file mode 100644 index 0000000000..0707dc045c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java @@ -0,0 +1,71 @@ +/* + * + * 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.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +public class SimpleQueueEntryImpl extends QueueEntryImpl +{ + volatile SimpleQueueEntryImpl _next; + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList) + { + super(queueEntryList); + } + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message, final long entryId) + { + super(queueEntryList, message, entryId); + } + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message) + { + super(queueEntryList, message); + } + + public SimpleQueueEntryImpl getNextNode() + { + return _next; + } + + public SimpleQueueEntryImpl getNextValidEntry() + { + + SimpleQueueEntryImpl next = getNextNode(); + while(next != null && next.isDispensed()) + { + + final SimpleQueueEntryImpl newNext = next.getNextNode(); + if(newNext != null) + { + SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext); + next = getNextNode(); + } + else + { + next = null; + } + + } + return next; + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index 46baab8c85..0bb5dcc219 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -1,10 +1,3 @@ -package org.apache.qpid.server.queue; - -import org.apache.qpid.server.message.ServerMessage; - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.util.concurrent.atomic.AtomicLong; - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -25,25 +18,31 @@ import java.util.concurrent.atomic.AtomicLong; * under the License. * */ -public class SimpleQueueEntryList implements QueueEntryList +package org.apache.qpid.server.queue; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.apache.qpid.server.message.ServerMessage; + +public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl> { - private final QueueEntryImpl _head; + private final SimpleQueueEntryImpl _head; - private volatile QueueEntryImpl _tail; + private volatile SimpleQueueEntryImpl _tail; - static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, QueueEntryImpl> + static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, SimpleQueueEntryImpl> _tailUpdater = AtomicReferenceFieldUpdater.newUpdater - (SimpleQueueEntryList.class, QueueEntryImpl.class, "_tail"); + (SimpleQueueEntryList.class, SimpleQueueEntryImpl.class, "_tail"); private final AMQQueue _queue; - static final AtomicReferenceFieldUpdater<QueueEntryImpl, QueueEntryImpl> + static final AtomicReferenceFieldUpdater<SimpleQueueEntryImpl, SimpleQueueEntryImpl> _nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (QueueEntryImpl.class, QueueEntryImpl.class, "_next"); + (SimpleQueueEntryImpl.class, SimpleQueueEntryImpl.class, "_next"); private AtomicLong _scavenges = new AtomicLong(0L); private final long _scavengeCount = Integer.getInteger("qpid.queue.scavenge_count", 50); @@ -52,14 +51,14 @@ public class SimpleQueueEntryList implements QueueEntryList public SimpleQueueEntryList(AMQQueue queue) { _queue = queue; - _head = new QueueEntryImpl(this); + _head = new SimpleQueueEntryImpl(this); _tail = _head; } void advanceHead() { - QueueEntryImpl next = _head.nextNode(); - QueueEntryImpl newNext = _head.getNext(); + SimpleQueueEntryImpl next = _head.getNextNode(); + SimpleQueueEntryImpl newNext = _head.getNextValidEntry(); if (next == newNext) { @@ -73,11 +72,11 @@ public class SimpleQueueEntryList implements QueueEntryList void scavenge() { - QueueEntryImpl next = _head.getNext(); + SimpleQueueEntryImpl next = _head.getNextValidEntry(); while (next != null) { - next = next.getNext(); + next = next.getNextValidEntry(); } } @@ -88,13 +87,13 @@ public class SimpleQueueEntryList implements QueueEntryList } - public QueueEntry add(ServerMessage message) + public SimpleQueueEntryImpl add(ServerMessage message) { - QueueEntryImpl node = createQueueEntry(message); + SimpleQueueEntryImpl node = createQueueEntry(message); for (;;) { - QueueEntryImpl tail = _tail; - QueueEntryImpl next = tail.nextNode(); + SimpleQueueEntryImpl tail = _tail; + SimpleQueueEntryImpl next = tail.getNextNode(); if (tail == _tail) { if (next == null) @@ -115,23 +114,22 @@ public class SimpleQueueEntryList implements QueueEntryList } } - protected QueueEntryImpl createQueueEntry(ServerMessage message) + protected SimpleQueueEntryImpl createQueueEntry(ServerMessage message) { - return new QueueEntryImpl(this, message); + return new SimpleQueueEntryImpl(this, message); } - public QueueEntry next(QueueEntry node) + public SimpleQueueEntryImpl next(SimpleQueueEntryImpl node) { - return ((QueueEntryImpl)node).getNext(); + return node.getNextValidEntry(); } - - public static class QueueEntryIteratorImpl implements QueueEntryIterator + public static class QueueEntryIteratorImpl implements QueueEntryIterator<SimpleQueueEntryImpl> { - private QueueEntryImpl _lastNode; + private SimpleQueueEntryImpl _lastNode; - QueueEntryIteratorImpl(QueueEntryImpl startNode) + QueueEntryIteratorImpl(SimpleQueueEntryImpl startNode) { _lastNode = startNode; } @@ -139,14 +137,12 @@ public class SimpleQueueEntryList implements QueueEntryList public boolean atTail() { - return _lastNode.nextNode() == null; + return _lastNode.getNextNode() == null; } - public QueueEntry getNode() + public SimpleQueueEntryImpl getNode() { - return _lastNode; - } public boolean advance() @@ -154,10 +150,10 @@ public class SimpleQueueEntryList implements QueueEntryList if(!atTail()) { - QueueEntryImpl nextNode = _lastNode.nextNode(); - while(nextNode.isDispensed() && nextNode.nextNode() != null) + SimpleQueueEntryImpl nextNode = _lastNode.getNextNode(); + while(nextNode.isDispensed() && nextNode.getNextNode() != null) { - nextNode = nextNode.nextNode(); + nextNode = nextNode.getNextNode(); } _lastNode = nextNode; return true; @@ -173,21 +169,26 @@ public class SimpleQueueEntryList implements QueueEntryList } - public QueueEntryIterator iterator() + public QueueEntryIteratorImpl iterator() { return new QueueEntryIteratorImpl(_head); } - public QueueEntry getHead() + public SimpleQueueEntryImpl getHead() { return _head; } + public void entryDeleted(SimpleQueueEntryImpl queueEntry) + { + advanceHead(); + } + static class Factory implements QueueEntryListFactory { - public QueueEntryList createQueueEntryList(AMQQueue queue) + public SimpleQueueEntryList createQueueEntryList(AMQQueue queue) { return new SimpleQueueEntryList(queue); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java new file mode 100644 index 0000000000..3f02442704 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java @@ -0,0 +1,30 @@ +package org.apache.qpid.server.queue; + +import java.util.Map; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SortedQueue extends OutOfOrderQueue +{ + private String _sortedPropertyName; + + protected SortedQueue(final String name, final boolean durable, + final String owner, final boolean autoDelete, final boolean exclusive, + final VirtualHost virtualHost, Map<String, Object> arguments, String sortedPropertyName) + { + super(name, durable, owner, autoDelete, exclusive, virtualHost, + new SortedQueueEntryListFactory(sortedPropertyName), arguments); + this._sortedPropertyName = sortedPropertyName; + } + + public String getSortedPropertyName() + { + return _sortedPropertyName; + } + + public synchronized void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException + { + super.enqueue(message, action); + } +}
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java new file mode 100644 index 0000000000..1052adbe67 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java @@ -0,0 +1,143 @@ +/* + * 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.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +/** + * An implementation of QueueEntryImpl to be used in SortedQueueEntryList. + */ +public class SortedQueueEntryImpl extends QueueEntryImpl +{ + public static enum Colour + { + RED, BLACK + }; + + private volatile SortedQueueEntryImpl _next; + private SortedQueueEntryImpl _prev; + private String _key; + + private Colour _colour = Colour.BLACK; + private SortedQueueEntryImpl _parent; + private SortedQueueEntryImpl _left; + private SortedQueueEntryImpl _right; + + public SortedQueueEntryImpl(final SortedQueueEntryList queueEntryList) + { + super(queueEntryList); + } + + public SortedQueueEntryImpl(final SortedQueueEntryList queueEntryList, + final ServerMessage message, final long entryId) + { + super(queueEntryList, message, entryId); + } + + @Override + public int compareTo(final QueueEntry o) + { + final String otherKey = ((SortedQueueEntryImpl) o)._key; + final int compare = _key == null ? (otherKey == null ? 0 : -1) : otherKey == null ? 1 : _key.compareTo(otherKey); + return compare == 0 ? super.compareTo(o) : compare; + } + + public Colour getColour() + { + return _colour; + } + + public String getKey() + { + return _key; + } + + public SortedQueueEntryImpl getLeft() + { + return _left; + } + + public SortedQueueEntryImpl getNextNode() + { + return _next; + } + + @Override + public SortedQueueEntryImpl getNextValidEntry() + { + return getNextNode(); + } + + public SortedQueueEntryImpl getParent() + { + return _parent; + } + + public SortedQueueEntryImpl getPrev() + { + return _prev; + } + + public SortedQueueEntryImpl getRight() + { + return _right; + } + + public void setColour(final Colour colour) + { + _colour = colour; + } + + public void setKey(final String key) + { + _key = key; + } + + public void setLeft(final SortedQueueEntryImpl left) + { + _left = left; + } + + public void setNext(final SortedQueueEntryImpl next) + { + _next = next; + } + + public void setParent(final SortedQueueEntryImpl parent) + { + _parent = parent; + } + + public void setPrev(final SortedQueueEntryImpl prev) + { + _prev = prev; + } + + public void setRight(final SortedQueueEntryImpl right) + { + _right = right; + } + + @Override + public String toString() + { + return "(" + (_colour == Colour.RED ? "Red," : "Black,") + _key + ")"; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java new file mode 100644 index 0000000000..5f8ab16c06 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java @@ -0,0 +1,665 @@ +/* + * + * 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.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.server.queue.SortedQueueEntryImpl.Colour; +import org.apache.qpid.server.store.StoreContext; + +/** + * A sorted implementation of QueueEntryList. + * Uses the red/black tree algorithm specified in "Introduction to Algorithms". + * ISBN-10: 0262033844 + * ISBN-13: 978-0262033848 + * @see http://en.wikipedia.org/wiki/Red-black_tree + */ +public class SortedQueueEntryList implements QueueEntryList<SortedQueueEntryImpl> +{ + private final SortedQueueEntryImpl _head; + private SortedQueueEntryImpl _root; + private long _entryId = Long.MIN_VALUE; + private final Object _lock = new Object(); + private final AMQQueue _queue; + private final String _propertyName; + + public SortedQueueEntryList(final AMQQueue queue, final String propertyName) + { + _queue = queue; + _head = new SortedQueueEntryImpl(this); + _propertyName = propertyName; + } + + @Override + public AMQQueue getQueue() + { + return _queue; + } + + @Override + public SortedQueueEntryImpl add(final ServerMessage message) + { + synchronized(_lock) + { + String key = null; + final Object val = message.getMessageHeader().getHeader(_propertyName); + if(val != null) + { + key = val.toString(); + } + + final SortedQueueEntryImpl entry = new SortedQueueEntryImpl(this,message, ++_entryId); + entry.setKey(key); + + insert(entry); + + return entry; + } + } + + /** + * Red Black Tree insert implementation. + * @param entry the entry to insert. + */ + private void insert(final SortedQueueEntryImpl entry) + { + SortedQueueEntryImpl node = _root; + if((node = _root) == null) + { + _root = entry; + _head.setNext(entry); + entry.setPrev(_head); + return; + } + else + { + SortedQueueEntryImpl parent = null; + while(node != null) + { + parent = node; + if(entry.compareTo(node) < 0) + { + node = node.getLeft(); + } + else + { + node = node.getRight(); + } + } + entry.setParent(parent); + + if(entry.compareTo(parent) < 0) + { + parent.setLeft(entry); + final SortedQueueEntryImpl prev = parent.getPrev(); + entry.setNext(parent); + prev.setNext(entry); + entry.setPrev(prev); + parent.setPrev(entry); + } + else + { + parent.setRight(entry); + + final SortedQueueEntryImpl next = parent.getNextValidEntry(); + entry.setNext(next); + parent.setNext(entry); + + if(next != null) + { + next.setPrev(entry); + } + entry.setPrev(parent); + } + } + entry.setColour(Colour.RED); + insertFixup(entry); + } + + private void insertFixup(SortedQueueEntryImpl entry) + { + while(isParentColour(entry, Colour.RED)) + { + final SortedQueueEntryImpl grandparent = nodeGrandparent(entry); + + if(nodeParent(entry) == leftChild(grandparent)) + { + final SortedQueueEntryImpl y = rightChild(grandparent); + if(isNodeColour(y, Colour.RED)) + { + setColour(nodeParent(entry), Colour.BLACK); + setColour(y, Colour.BLACK); + setColour(grandparent, Colour.RED); + entry = grandparent; + } + else + { + if(entry == rightChild(nodeParent(entry))) + { + entry = nodeParent(entry); + leftRotate(entry); + } + setColour(nodeParent(entry), Colour.BLACK); + setColour(nodeGrandparent(entry), Colour.RED); + rightRotate(nodeGrandparent(entry)); + } + } + else + { + final SortedQueueEntryImpl y = leftChild(grandparent); + if(isNodeColour(y, Colour.RED)) + { + setColour(nodeParent(entry), Colour.BLACK); + setColour(y, Colour.BLACK); + setColour(grandparent, Colour.RED); + entry = grandparent; + } + else + { + if(entry == leftChild(nodeParent(entry))) + { + entry = nodeParent(entry); + rightRotate(entry); + } + setColour(nodeParent(entry), Colour.BLACK); + setColour(nodeGrandparent(entry), Colour.RED); + leftRotate(nodeGrandparent(entry)); + } + } + } + _root.setColour(Colour.BLACK); + } + + private void leftRotate(final SortedQueueEntryImpl entry) + { + if(entry != null) + { + final SortedQueueEntryImpl rightChild = rightChild(entry); + entry.setRight(rightChild.getLeft()); + if(entry.getRight() != null) + { + entry.getRight().setParent(entry); + } + rightChild.setParent(entry.getParent()); + if(entry.getParent() == null) + { + _root = rightChild; + } + else if(entry == entry.getParent().getLeft()) + { + entry.getParent().setLeft(rightChild); + } + else + { + entry.getParent().setRight(rightChild); + } + rightChild.setLeft(entry); + entry.setParent(rightChild); + } + } + + private void rightRotate(final SortedQueueEntryImpl entry) + { + if(entry != null) + { + final SortedQueueEntryImpl leftChild = leftChild(entry); + entry.setLeft(leftChild.getRight()); + if(entry.getLeft() != null) + { + leftChild.getRight().setParent(entry); + } + leftChild.setParent(entry.getParent()); + if(leftChild.getParent() == null) + { + _root = leftChild; + } + else if(entry == entry.getParent().getRight()) + { + entry.getParent().setRight(leftChild); + } + else + { + entry.getParent().setLeft(leftChild); + } + leftChild.setRight(entry); + entry.setParent(leftChild); + } + } + + private void setColour(final SortedQueueEntryImpl node, final Colour colour) + { + if(node != null) + { + node.setColour(colour); + } + } + + private SortedQueueEntryImpl leftChild(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getLeft(); + } + + private SortedQueueEntryImpl rightChild(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getRight(); + } + + private SortedQueueEntryImpl nodeParent(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getParent(); + } + + private SortedQueueEntryImpl nodeGrandparent(final SortedQueueEntryImpl node) + { + return nodeParent(nodeParent(node)); + } + + private boolean isParentColour(final SortedQueueEntryImpl node, final SortedQueueEntryImpl.Colour colour) + { + + return node != null && isNodeColour(node.getParent(), colour); + } + + protected boolean isNodeColour(final SortedQueueEntryImpl node, final SortedQueueEntryImpl.Colour colour) + { + return (node == null ? Colour.BLACK : node.getColour()) == colour; + } + + @Override + public SortedQueueEntryImpl next(final SortedQueueEntryImpl node) + { + synchronized(_lock) + { + if(node.isDispensed() && _head != node) + { + SortedQueueEntryImpl current = _head; + SortedQueueEntryImpl next; + while(current != null) + { + next = current.getNextValidEntry(); + if(current.compareTo(node)>0 && !current.isDispensed()) + { + break; + } + else + { + current = next; + } + } + return current; + } + else + { + return node.getNextValidEntry(); + } + } + } + + @Override + public QueueEntryIterator<SortedQueueEntryImpl> iterator() + { + return new QueueEntryIteratorImpl(_head); + } + + @Override + public SortedQueueEntryImpl getHead() + { + return _head; + } + + protected SortedQueueEntryImpl getRoot() + { + return _root; + } + + @Override + public void entryDeleted(final SortedQueueEntryImpl entry) + { + synchronized(_lock) + { + // If the node to be removed has two children, we swap the position + // of the node and its successor in the tree + if(leftChild(entry) != null && rightChild(entry) != null) + { + swapWithSuccessor(entry); + } + + // Then deal with the easy doubly linked list deletion (need to do + // this after the swap as the swap uses next + final SortedQueueEntryImpl prev = entry.getPrev(); + if(prev != null) + { + prev.setNext(entry.getNextValidEntry()); + } + + final SortedQueueEntryImpl next = entry.getNextValidEntry(); + if(next != null) + { + next.setPrev(prev); + } + + // now deal with splicing + final SortedQueueEntryImpl chosenChild; + + if(leftChild(entry) != null) + { + chosenChild = leftChild(entry); + } + else + { + chosenChild = rightChild(entry); + } + + if(chosenChild != null) + { + // we have one child (x), we can move it up to replace x; + chosenChild.setParent(entry.getParent()); + if(chosenChild.getParent() == null) + { + _root = chosenChild; + } + else if(entry == entry.getParent().getLeft()) + { + entry.getParent().setLeft(chosenChild); + } + else + { + entry.getParent().setRight(chosenChild); + } + + entry.setLeft(null); + entry.setRight(null); + entry.setParent(null); + + if(entry.getColour() == Colour.BLACK) + { + deleteFixup(chosenChild); + } + + } + else + { + // no children + if(entry.getParent() == null) + { + // no parent either - the tree is empty + _root = null; + } + else + { + if(entry.getColour() == Colour.BLACK) + { + deleteFixup(entry); + } + + if(entry.getParent() != null) + { + if(entry.getParent().getLeft() == entry) + { + entry.getParent().setLeft(null); + } + else if(entry.getParent().getRight() == entry) + { + entry.getParent().setRight(null); + } + entry.setParent(null); + } + } + } + + } + } + + /** + * Swaps the position of the node in the tree with it's successor + * (that is the node with the next highest key) + * @param entry + */ + private void swapWithSuccessor(final SortedQueueEntryImpl entry) + { + final SortedQueueEntryImpl next = entry.getNextValidEntry(); + final SortedQueueEntryImpl nextParent = next.getParent(); + final SortedQueueEntryImpl nextLeft = next.getLeft(); + final SortedQueueEntryImpl nextRight = next.getRight(); + final Colour nextColour = next.getColour(); + + // Special case - the successor is the right child of the node + if(next == entry.getRight()) + { + next.setParent(entry.getParent()); + if(next.getParent() == null) + { + _root = next; + } + else if(next.getParent().getLeft() == entry) + { + next.getParent().setLeft(next); + } + else + { + next.getParent().setRight(next); + } + + next.setRight(entry); + entry.setParent(next); + next.setLeft(entry.getLeft()); + + if(next.getLeft() != null) + { + next.getLeft().setParent(next); + } + + next.setColour(entry.getColour()); + entry.setColour(nextColour); + entry.setLeft(nextLeft); + + if(nextLeft != null) + { + nextLeft.setParent(entry); + } + entry.setRight(nextRight); + if(nextRight != null) + { + nextRight.setParent(entry); + } + } + else + { + next.setParent(entry.getParent()); + if(next.getParent() == null) + { + _root = next; + } + else if(next.getParent().getLeft() == entry) + { + next.getParent().setLeft(next); + } + else + { + next.getParent().setRight(next); + } + + next.setLeft(entry.getLeft()); + if(next.getLeft() != null) + { + next.getLeft().setParent(next); + } + next.setRight(entry.getRight()); + if(next.getRight() != null) + { + next.getRight().setParent(next); + } + next.setColour(entry.getColour()); + + entry.setParent(nextParent); + if(nextParent.getLeft() == next) + { + nextParent.setLeft(entry); + } + else + { + nextParent.setRight(entry); + } + + entry.setLeft(nextLeft); + if(nextLeft != null) + { + nextLeft.setParent(entry); + } + entry.setRight(nextRight); + if(nextRight != null) + { + nextRight.setParent(entry); + } + entry.setColour(nextColour); + } + } + + private void deleteFixup(SortedQueueEntryImpl entry) + { + int i = 0; + while(entry != null && entry != _root + && isNodeColour(entry, Colour.BLACK)) + { + i++; + + if(i > 1000) + { + return; + } + + if(entry == leftChild(nodeParent(entry))) + { + SortedQueueEntryImpl rightSibling = rightChild(nodeParent(entry)); + if(isNodeColour(rightSibling, Colour.RED)) + { + setColour(rightSibling, Colour.BLACK); + nodeParent(entry).setColour(Colour.RED); + leftRotate(nodeParent(entry)); + rightSibling = rightChild(nodeParent(entry)); + } + + if(isNodeColour(leftChild(rightSibling), Colour.BLACK) + && isNodeColour(rightChild(rightSibling), Colour.BLACK)) + { + setColour(rightSibling, Colour.RED); + entry = nodeParent(entry); + } + else + { + if(isNodeColour(rightChild(rightSibling), Colour.BLACK)) + { + setColour(leftChild(rightSibling), Colour.BLACK); + rightSibling.setColour(Colour.RED); + rightRotate(rightSibling); + rightSibling = rightChild(nodeParent(entry)); + } + setColour(rightSibling, getColour(nodeParent(entry))); + setColour(nodeParent(entry), Colour.BLACK); + setColour(rightChild(rightSibling), Colour.BLACK); + leftRotate(nodeParent(entry)); + entry = _root; + } + } + else + { + SortedQueueEntryImpl leftSibling = leftChild(nodeParent(entry)); + if(isNodeColour(leftSibling, Colour.RED)) + { + setColour(leftSibling, Colour.BLACK); + nodeParent(entry).setColour(Colour.RED); + rightRotate(nodeParent(entry)); + leftSibling = leftChild(nodeParent(entry)); + } + + if(isNodeColour(leftChild(leftSibling), Colour.BLACK) + && isNodeColour(rightChild(leftSibling), Colour.BLACK)) + { + setColour(leftSibling, Colour.RED); + entry = nodeParent(entry); + } + else + { + if(isNodeColour(leftChild(leftSibling), Colour.BLACK)) + { + setColour(rightChild(leftSibling), Colour.BLACK); + leftSibling.setColour(Colour.RED); + leftRotate(leftSibling); + leftSibling = leftChild(nodeParent(entry)); + } + setColour(leftSibling, getColour(nodeParent(entry))); + setColour(nodeParent(entry), Colour.BLACK); + setColour(leftChild(leftSibling), Colour.BLACK); + rightRotate(nodeParent(entry)); + entry = _root; + } + } + } + setColour(entry, Colour.BLACK); + } + + private Colour getColour(final SortedQueueEntryImpl x) + { + return x == null ? null : x.getColour(); + } + + public class QueueEntryIteratorImpl implements QueueEntryIterator<SortedQueueEntryImpl> + { + private SortedQueueEntryImpl _lastNode; + + public QueueEntryIteratorImpl(final SortedQueueEntryImpl startNode) + { + _lastNode = startNode; + } + + public boolean atTail() + { + return next(_lastNode) == null; + } + + public SortedQueueEntryImpl getNode() + { + return _lastNode; + } + + public boolean advance() + { + if(!atTail()) + { + SortedQueueEntryImpl nextNode = next(_lastNode); + while(nextNode.isDispensed() && next(nextNode) != null) + { + nextNode = next(nextNode); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java new file mode 100644 index 0000000000..7a70795e77 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java @@ -0,0 +1,19 @@ +package org.apache.qpid.server.queue; + +public class SortedQueueEntryListFactory implements QueueEntryListFactory +{ + + private final String _propertyName; + + public SortedQueueEntryListFactory(final String propertyName) + { + _propertyName = propertyName; + } + + @Override + public QueueEntryList<SortedQueueEntryImpl> createQueueEntryList(final AMQQueue queue) + { + return new SortedQueueEntryList(queue, _propertyName); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java index 46c1a6af9a..fbef23dca1 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java @@ -27,6 +27,10 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.AMQException; import org.apache.log4j.Logger; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + class SubFlushRunner implements ReadWriteRunnable { @@ -34,29 +38,33 @@ class SubFlushRunner implements ReadWriteRunnable private final Subscription _sub; - private final String _name; + + private static int IDLE = 0; + private static int SCHEDULED = 1; + private static int RUNNING = 2; + + + private final AtomicInteger _scheduled = new AtomicInteger(IDLE); + + private static final long ITERATIONS = SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + private final AtomicBoolean _stateChange = new AtomicBoolean(); public SubFlushRunner(Subscription sub) { _sub = sub; - _name = "SubFlushRunner-"+_sub; } public void run() { - - String originalName = Thread.currentThread().getName(); - try + if(_scheduled.compareAndSet(SCHEDULED, RUNNING)) { - Thread.currentThread().setName(_name); - boolean complete = false; + _stateChange.set(false); try { CurrentActor.set(_sub.getLogActor()); complete = getQueue().flushSubscription(_sub, ITERATIONS); - } catch (AMQException e) { @@ -66,17 +74,15 @@ class SubFlushRunner implements ReadWriteRunnable { CurrentActor.remove(); } - if (!complete && !_sub.isSuspended()) + _scheduled.compareAndSet(RUNNING, IDLE); + if ((!complete || _stateChange.compareAndSet(true,false))&& !_sub.isSuspended()) { - getQueue().execute(this); + if(_scheduled.compareAndSet(IDLE,SCHEDULED)) + { + getQueue().execute(this); + } } - } - finally - { - Thread.currentThread().setName(originalName); - } - } private SimpleAMQQueue getQueue() @@ -93,4 +99,18 @@ class SubFlushRunner implements ReadWriteRunnable { return true; } + + public String toString() + { + return "SubFlushRunner-" + _sub.getLogActor(); + } + + public void execute(Executor executor) + { + _stateChange.set(true); + if(_scheduled.compareAndSet(IDLE,SCHEDULED)) + { + executor.execute(this); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index c07074f69c..7eb1b54693 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -250,14 +250,14 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { + initialiseManagedObjectRegistry(); + configure(); _qmfService = new QMFService(getConfigStore(), this); CurrentActor.get().message(BrokerMessages.STARTUP(QpidProperties.getReleaseVersion(), QpidProperties.getBuildVersion())); - initialiseManagedObjectRegistry(); - _virtualHostRegistry = new VirtualHostRegistry(this); _securityManager = new SecurityManager(_configuration, _pluginManager); @@ -471,12 +471,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry close(_authenticationManager); - close(_managedObjectRegistry); - close(_qmfService); close(_pluginManager); + close(_managedObjectRegistry); + CurrentActor.get().message(BrokerMessages.STOPPED()); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java index 8b5ff6781d..ec11e2d39c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java @@ -73,11 +73,6 @@ public abstract class AbstractProxyPlugin extends AbstractPlugin return getDefault(); } - public Result authoriseExecute(ObjectType object, ObjectProperties properties) - { - return getDefault(); - } - public Result authoriseUpdate(ObjectType object, ObjectProperties properties) { return getDefault(); @@ -121,8 +116,6 @@ public abstract class AbstractProxyPlugin extends AbstractPlugin return authoriseDelete(objectType, properties); case PURGE: return authorisePurge(objectType, properties); - case EXECUTE: - return authoriseExecute(objectType, properties); case UPDATE: return authoriseUpdate(objectType, properties); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java index f582fed6a0..abf9e3379d 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -20,10 +20,8 @@ package org.apache.qpid.server.security; import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; import static org.apache.qpid.server.security.access.ObjectType.METHOD; -import static org.apache.qpid.server.security.access.ObjectType.OBJECT; import static org.apache.qpid.server.security.access.ObjectType.QUEUE; import static org.apache.qpid.server.security.access.ObjectType.VIRTUALHOST; -import static org.apache.qpid.server.security.access.Operation.ACCESS; import static org.apache.qpid.server.security.access.Operation.BIND; import static org.apache.qpid.server.security.access.Operation.CONSUME; import static org.apache.qpid.server.security.access.Operation.CREATE; @@ -67,7 +65,14 @@ public class SecurityManager /** Container for the {@link Principal} that is using to this thread. */ private static final ThreadLocal<Subject> _subject = new ThreadLocal<Subject>(); - + private static final ThreadLocal<Boolean> _accessChecksDisabled = new ThreadLocal<Boolean>() + { + protected Boolean initialValue() + { + return false; + } + }; + private PluginManager _pluginManager; private Map<String, SecurityPluginFactory> _pluginFactories = new HashMap<String, SecurityPluginFactory>(); private Map<String, SecurityPlugin> _globalPlugins = new HashMap<String, SecurityPlugin>(); @@ -194,6 +199,11 @@ public class SecurityManager private boolean checkAllPlugins(AccessCheck checker) { + if(_accessChecksDisabled.get()) + { + return true; + } + HashMap<String, SecurityPlugin> remainingPlugins = new HashMap<String, SecurityPlugin>(_globalPlugins); for (Entry<String, SecurityPlugin> hostEntry : _hostPlugins.entrySet()) @@ -273,21 +283,6 @@ public class SecurityManager } }); } - - // TODO not implemented yet, awaiting consensus - public boolean authoriseObject(final String packageName, final String className) - { - return checkAllPlugins(new AccessCheck() - { - Result allowed(SecurityPlugin plugin) - { - ObjectProperties properties = new ObjectProperties(); - properties.put(ObjectProperties.Property.PACKAGE, packageName); - properties.put(ObjectProperties.Property.CLASS, className); - return plugin.authorise(ACCESS, OBJECT, properties); - } - }); - } public boolean authoriseMethod(final Operation operation, final String componentName, final String methodName) { @@ -329,17 +324,6 @@ public class SecurityManager }); } - public boolean authoriseConsume(final boolean exclusive, final boolean noAck, final boolean noLocal, final boolean nowait, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - Result allowed(SecurityPlugin plugin) - { - return plugin.authorise(CONSUME, QUEUE, new ObjectProperties(exclusive, noAck, noLocal, nowait, queue)); - } - }); - } - public boolean authoriseCreateExchange(final Boolean autoDelete, final Boolean durable, final AMQShortString exchangeName, final Boolean internal, final Boolean nowait, final Boolean passive, final AMQShortString exchangeType) { @@ -419,4 +403,14 @@ public class SecurityManager } }); } + + public static boolean setAccessChecksDisabled(final boolean status) + { + //remember current value + boolean current = _accessChecksDisabled.get(); + + _accessChecksDisabled.set(status); + + return current; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java index 7103b30283..69c7ff185a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java @@ -32,14 +32,12 @@ import java.util.Set; public enum ObjectType { ALL(Operation.ALL), - VIRTUALHOST(ACCESS), - QUEUE(CREATE, DELETE, PURGE, CONSUME), - TOPIC(CREATE, DELETE, PURGE, CONSUME), - EXCHANGE(ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), + VIRTUALHOST(Operation.ALL, ACCESS), + QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME), + EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), LINK, // Not allowed in the Java broker ROUTE, // Not allowed in the Java broker - METHOD(Operation.ALL, ACCESS, UPDATE, EXECUTE), - OBJECT(ACCESS); + METHOD(Operation.ALL, ACCESS, UPDATE); private EnumSet<Operation> _actions; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java index 06d7f8fd0c..21ea042eed 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java @@ -32,8 +32,7 @@ public enum Operation UNBIND, DELETE, PURGE, - UPDATE, - EXECUTE; + UPDATE; public static Operation parse(String text) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java index 1612d13b1b..7cb34da804 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java @@ -384,16 +384,8 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase BufferedReader reader = null; PrintStream writer = null; - - Random r = new Random(); - File tmp; - do - { - tmp = new File(_passwordFile.getPath() + r.nextInt() + ".tmp"); - } - while(tmp.exists()); - - tmp.deleteOnExit(); + + final File tmp = createTempFileOnSameFilesystem(_passwordFile); try { @@ -458,52 +450,72 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase } finally { - if (reader != null) - { - reader.close(); - } - if (writer != null) { writer.close(); } - } - - // Swap temp file to main password file. - File old = new File(_passwordFile.getAbsoluteFile() + ".old"); - if (old.exists()) - { - old.delete(); - } - - if(!_passwordFile.renameTo(old)) - { - //unable to rename the existing file to the backup name - _logger.error("Could not backup the existing password file"); - throw new IOException("Could not backup the existing password file"); - } - - if(!tmp.renameTo(_passwordFile)) - { - //failed to rename the new file to the required filename - - if(!old.renameTo(_passwordFile)) + if (reader != null) { - //unable to return the backup to required filename - _logger.error("Could not rename the new password file into place, and unable to restore original file"); - throw new IOException("Could not rename the new password file into place, and unable to restore original file"); + reader.close(); } - - _logger.error("Could not rename the new password file into place"); - throw new IOException("Could not rename the new password file into place"); } - + + swapTempFileToLive(_passwordFile, tmp); + } finally { _userUpdate.unlock(); } } + + private void swapTempFileToLive(final File live, final File temp) throws IOException + { + // Remove any existing ".old" file + final File old = new File(live.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + + // Create an new ".old" file + if(!live.renameTo(old)) + { + //unable to rename the existing file to the backup name + _logger.error("Could not backup the existing password file"); + throw new IOException("Could not backup the existing password file"); + } + + // Move temp file to be the new "live" file + if(!temp.renameTo(live)) + { + //failed to rename the new file to the required filename + if(!old.renameTo(live)) + { + //unable to return the backup to required filename + _logger.error("Could not rename the new password file into place, and unable to restore original file"); + throw new IOException("Could not rename the new password file into place, and unable to restore original file"); + } + + _logger.error("Could not rename the new password file into place"); + throw new IOException("Could not rename the new password file into place"); + } + } + + private File createTempFileOnSameFilesystem(final File liveFile) + { + File tmp; + final Random r = new Random(); + + do + { + tmp = new File(liveFile.getPath() + r.nextInt() + ".tmp"); + } + while(tmp.exists()); + + tmp.deleteOnExit(); + return tmp; + } public void reload() throws IOException { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java index 8b099b62ce..d3f46d2e90 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java @@ -142,6 +142,19 @@ public class DerbyMessageStore implements MessageStore private boolean _configured; + private static final class CommitStoreFuture implements StoreFuture + { + public boolean isComplete() + { + return true; + } + + public void waitForCompletion() + { + + } + } + private enum State { INITIAL, @@ -910,19 +923,19 @@ public class DerbyMessageStore implements MessageStore throws AMQStoreException { Connection conn = null; + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); + stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); stmt.setString(1, exchange.getNameShortString().toString() ); stmt.setString(2, queue.getNameShortString().toString()); stmt.setString(3, routingKey == null ? null : routingKey.toString()); - + int result = stmt.executeUpdate(); - stmt.close(); - + if(result != 1) { throw new AMQStoreException("Queue binding for queue with name " + queue.getNameShortString() + " to exchange " @@ -936,21 +949,9 @@ public class DerbyMessageStore implements MessageStore } finally { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error(e); - } - } - + closePreparedStatement(stmt); + closeConnection(conn); } - - } public void createQueue(AMQQueue queue) throws AMQStoreException @@ -1153,15 +1154,14 @@ public class DerbyMessageStore implements MessageStore AMQShortString name = queue.getNameShortString(); _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); Connection conn = null; - + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE); + stmt = conn.prepareStatement(DELETE_FROM_QUEUE); stmt.setString(1, name.toString()); int results = stmt.executeUpdate(); - stmt.close(); - + if (results == 0) { throw new AMQStoreException("Queue " + name + " not found"); @@ -1173,18 +1173,8 @@ public class DerbyMessageStore implements MessageStore } finally { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error(e); - } - } - + closePreparedStatement(stmt); + closeConnection(conn); } @@ -1317,19 +1307,7 @@ public class DerbyMessageStore implements MessageStore public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException { commitTran(connWrapper); - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - + return new CommitStoreFuture(); } public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException @@ -1572,6 +1550,7 @@ public class DerbyMessageStore implements MessageStore { _logger.debug("Adding content chunk offset " + offset + " for message " +messageId); } + PreparedStatement stmt = null; try { @@ -1580,7 +1559,7 @@ public class DerbyMessageStore implements MessageStore byte[] chunkData = new byte[src.limit()]; src.duplicate().get(chunkData); - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); stmt.setLong(1,messageId); stmt.setInt(2, offset); stmt.setInt(3, offset+chunkData.length); @@ -1594,24 +1573,16 @@ public class DerbyMessageStore implements MessageStore ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); stmt.setBinaryStream(4, bis, chunkData.length); stmt.executeUpdate(); - stmt.close(); } catch (SQLException e) { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e1) - { - - } - } - + closeConnection(conn); throw new RuntimeException("Error adding content chunk offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); } + finally + { + closePreparedStatement(stmt); + } } @@ -1619,13 +1590,13 @@ public class DerbyMessageStore implements MessageStore public int getContent(long messageId, int offset, ByteBuffer dst) { Connection conn = null; - + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); stmt.setLong(1,messageId); stmt.setInt(2, offset); stmt.setInt(3, offset+dst.remaining()); @@ -1656,28 +1627,18 @@ public class DerbyMessageStore implements MessageStore } } - stmt.close(); - conn.close(); return written; } catch (SQLException e) { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e1) - { - - } - } - throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); } - + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } } @@ -1849,5 +1810,33 @@ public class DerbyMessageStore implements MessageStore } } + private void closeConnection(final Connection conn) + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error("Problem closing connection", e); + } + } + } + private void closePreparedStatement(final PreparedStatement stmt) + { + if (stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + _logger.error("Problem closing prepared statement", e); + } + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java index b49b12fb79..80c5e2866c 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java @@ -53,12 +53,12 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi } - public void onRelease() + public void onRelease(boolean setRedelivered) { final Subscription_0_10 subscription = getSubscription(); if(subscription != null && _entry.isAcquiredBy(_sub)) { - subscription.release(_entry); + subscription.release(_entry, setRedelivered); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java index b5bb2014b5..a61b0b4e82 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java @@ -43,11 +43,11 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi _logger.warn("MessageAccept received for message which is using NONE as the accept mode (likely client error)"); } - public void onRelease() + public void onRelease(boolean setRedelivered) { if(_entry.isAcquiredBy(_sub)) { - getSubscription().release(_entry); + getSubscription().release(_entry, setRedelivered); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index 5c4e413989..bc1be90531 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -69,17 +69,24 @@ public interface Subscription void close(); - void send(QueueEntry msg) throws AMQException; + void send(QueueEntry entry, boolean batch) throws AMQException; + + void flushBatched(); void queueDeleted(AMQQueue queue); boolean wouldSuspend(QueueEntry msg); + boolean trySendLock(); + + void getSendLock(); void releaseSendLock(); + void releaseQueueEntry(final QueueEntry queueEntryImpl); + void onDequeue(final QueueEntry queueEntry); void restoreCredit(final QueueEntry queueEntry); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index adb0a84151..23ae14eef1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -119,11 +119,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * This method can be called by each of the publisher threads. As a result all changes to the channel object must be * thread safe. * - * @param msg The message to send + * + * @param entry + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry msg) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { // We don't decrement the reference here as we don't want to consume the message // but we do want to send it to the client. @@ -131,7 +133,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { long deliveryTag = getChannel().getNextDeliveryTag(); - sendToClient(msg, deliveryTag); + sendToClient(entry, deliveryTag); } } @@ -173,11 +175,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * This method can be called by each of the publisher threads. As a result all changes to the channel object must be * thread safe. * + * * @param entry The message to send + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry entry) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { // if we do not need to wait for client acknowledgements // we can decrement the reference count immediately. @@ -193,6 +197,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { + getChannel().getProtocolSession().setDeferFlush(batch); long deliveryTag = getChannel().getNextDeliveryTag(); sendToClient(entry, deliveryTag); @@ -234,7 +239,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public boolean wouldSuspend(QueueEntry msg) { - return !getCreditManager().useCreditForMessage(msg.getMessage()); + return !getCreditManager().useCreditForMessage(msg.getMessage().getSize()); } } @@ -263,11 +268,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * This method can be called by each of the publisher threads. As a result all changes to the channel object must be * thread safe. * + * * @param entry The message to send + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry entry) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { // if we do not need to wait for client acknowledgements @@ -282,6 +289,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { + getChannel().getProtocolSession().setDeferFlush(batch); long deliveryTag = getChannel().getNextDeliveryTag(); @@ -441,10 +449,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * This method can be called by each of the publisher threads. As a result all changes to the channel object must be * thread safe. * - * @param msg The message to send + * + * @param entry + * @param batch * @throws AMQException */ - abstract public void send(QueueEntry msg) throws AMQException; + abstract public void send(QueueEntry entry, boolean batch) throws AMQException; public boolean isSuspended() @@ -575,7 +585,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public boolean wouldSuspend(QueueEntry msg) { - return !_creditManager.useCreditForMessage(msg.getMessage());//_channel.wouldSuspend(msg.getMessage()); + return !_creditManager.useCreditForMessage(msg.getMessage().getSize());//_channel.wouldSuspend(msg.getMessage()); + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); } public void getSendLock() @@ -623,13 +638,16 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage restoreCredit(queueEntry); } + public void releaseQueueEntry(final QueueEntry queueEntry) + { + restoreCredit(queueEntry); + } + public void restoreCredit(final QueueEntry queueEntry) { _creditManager.restoreCredit(1, queueEntry.getSize()); } - - public void creditStateChanged(boolean hasCredit) { @@ -821,4 +839,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage confirmAutoClose(); } } + + public void flushBatched() + { + _channel.getProtocolSession().setDeferFlush(false); + + _channel.getProtocolSession().flushBatched(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index d9845c5fa6..6db58ce9c1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -24,12 +24,15 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SUBSCRIPT import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.QUEUE_FORMAT; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.InboundMessageAdapter; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConfiguredObject; import org.apache.qpid.server.configuration.SessionConfig; import org.apache.qpid.server.configuration.SubscriptionConfig; import org.apache.qpid.server.configuration.SubscriptionConfigType; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.flow.CreditCreditManager; import org.apache.qpid.server.flow.WindowCreditManager; @@ -37,9 +40,11 @@ import org.apache.qpid.server.flow.FlowCreditManager_0_10; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.messages.SubscriptionMessages; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.AMQMessage; @@ -56,6 +61,8 @@ import org.apache.qpid.transport.MessageFlowMode; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.MessageTransfer; import org.apache.qpid.transport.Method; +import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.Session; import org.apache.qpid.transport.Struct; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -79,11 +86,14 @@ import java.nio.ByteBuffer; public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCreditManagerListener, SubscriptionConfig, LogSubject { + private final long _subscriptionID; private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); + private static final Option[] BATCHED = new Option[] { Option.BATCH }; + private final Lock _stateChangeLock = new ReentrantLock(); private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE); @@ -120,6 +130,8 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private final long _createTime = System.currentTimeMillis(); private final AtomicLong _deliveredCount = new AtomicLong(0); private final Map<String, Object> _arguments; + private int _deferredMessageCredit; + private long _deferredSizeCredit; public Subscription_0_10(ServerSession session, String destination, MessageAcceptMode acceptMode, @@ -130,6 +142,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { _subscriptionID = subscriptionId; _session = session; + _postIdSettingAction = new AddMessageDispositionListenerAction(session); _destination = destination; _acceptMode = acceptMode; _acquireMode = acquireMode; @@ -204,13 +217,13 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return false; } - - - if (_noLocal - && (entry.getMessage() instanceof MessageTransferMessage) - && ((MessageTransferMessage)entry.getMessage()).getSession() == _session) + if (_noLocal && entry.getMessage() instanceof MessageTransferMessage) { - return false; + Session messageSession= ((MessageTransferMessage)entry.getMessage()).getSession(); + if (messageSession != null && messageSession.getConnection() == _session.getConnection()) + { + return false; + } } @@ -307,10 +320,26 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } - private class AddMessageDispositionListnerAction implements Runnable + public static class AddMessageDispositionListenerAction implements Runnable { - public MessageTransfer _xfr; - public ServerSession.MessageDispositionChangeListener _action; + private MessageTransfer _xfr; + private ServerSession.MessageDispositionChangeListener _action; + private ServerSession _session; + + public AddMessageDispositionListenerAction(ServerSession session) + { + _session = session; + } + + public void setXfr(MessageTransfer xfr) + { + _xfr = xfr; + } + + public void setAction(ServerSession.MessageDispositionChangeListener action) + { + _action = action; + } public void run() { @@ -321,9 +350,9 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } } - private final AddMessageDispositionListnerAction _postIdSettingAction = new AddMessageDispositionListnerAction(); + private final AddMessageDispositionListenerAction _postIdSettingAction; - public void send(final QueueEntry entry) throws AMQException + public void send(final QueueEntry entry, boolean batch) throws AMQException { ServerMessage serverMsg = entry.getMessage(); @@ -568,27 +597,29 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { public void onComplete(Method method) { - restoreCredit(entry); + deferredAddCredit(1, entry.getSize()); } }); } - _postIdSettingAction._xfr = xfr; + _postIdSettingAction.setXfr(xfr); if(_acceptMode == MessageAcceptMode.EXPLICIT) { - _postIdSettingAction._action = new ExplicitAcceptDispositionChangeListener(entry, this); + _postIdSettingAction.setAction(new ExplicitAcceptDispositionChangeListener(entry, this)); } else if(_acquireMode != MessageAcquireMode.PRE_ACQUIRED) { - _postIdSettingAction._action = new ImplicitAcceptDispositionChangeListener(entry, this); + _postIdSettingAction.setAction(new ImplicitAcceptDispositionChangeListener(entry, this)); } else { - _postIdSettingAction._action = null; + _postIdSettingAction.setAction(null); } + _session.sendMessage(xfr, _postIdSettingAction); + entry.incrementDeliveryCount(); _deliveredCount.incrementAndGet(); if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) { @@ -624,17 +655,74 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr }); } - void reject(QueueEntry entry) + void reject(final QueueEntry entry) { entry.setRedelivered(); entry.routeToAlternate(); } - void release(QueueEntry entry) + void release(final QueueEntry entry, final boolean setRedelivered) { - entry.setRedelivered(); - entry.release(); + if (setRedelivered) + { + entry.setRedelivered(); + } + + if (getSession().isClosing() || !setRedelivered) + { + entry.decrementDeliveryCount(); + } + + if (isMaxDeliveryLimitReached(entry)) + { + sendToDLQOrDiscard(entry); + } + else + { + entry.release(); + } + } + + protected void sendToDLQOrDiscard(QueueEntry entry) + { + final Exchange alternateExchange = entry.getQueue().getAlternateExchange(); + final LogActor logActor = CurrentActor.get(); + final ServerMessage msg = entry.getMessage(); + if (alternateExchange != null) + { + final InboundMessage m = new InboundMessageAdapter(entry); + + final ArrayList<? extends BaseQueue> destinationQueues = alternateExchange.route(m); + + if (destinationQueues == null || destinationQueues.isEmpty()) + { + entry.discard(); + + logActor.message( ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), alternateExchange.getName())); + } + else + { + entry.routeToAlternate(); + + //output operational logging for each delivery post commit + for (final BaseQueue destinationQueue : destinationQueues) + { + logActor.message( ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), destinationQueue.getNameShortString().asString())); + } + } + } + else + { + entry.discard(); + logActor.message(ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), entry.getQueue().getName(), msg.getRoutingKey())); + } + } + + private boolean isMaxDeliveryLimitReached(QueueEntry entry) + { + final int maxDeliveryLimit = entry.getQueue().getMaximumDeliveryCount(); + return (maxDeliveryLimit > 0 && entry.getDeliveryCount() >= maxDeliveryLimit); } public void queueDeleted(AMQQueue queue) @@ -642,9 +730,14 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr _deleted.set(true); } - public boolean wouldSuspend(QueueEntry msg) + public boolean wouldSuspend(QueueEntry entry) { - return !_creditManager.useCreditForMessage(msg.getMessage()); + return !_creditManager.useCreditForMessage(entry.getMessage().getSize()); + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); } public void getSendLock() @@ -664,7 +757,12 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void onDequeue(QueueEntry queueEntry) { + // no-op for 0-10, credit restored by completing command. + } + public void releaseQueueEntry(QueueEntry queueEntry) + { + // no-op for 0-10, credit restored by completing command. } public void setStateListener(StateListener listener) @@ -702,6 +800,28 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return _properties.get(key); } + private void deferredAddCredit(final int deferredMessageCredit, final long deferredSizeCredit) + { + _deferredMessageCredit += deferredMessageCredit; + _deferredSizeCredit += deferredSizeCredit; + + } + + public void flushCreditState() + { + flushCreditState(false); + } + public void flushCreditState(boolean strict) + { + if(strict || !isSuspended() || _deferredMessageCredit >= 200 + || !(_creditManager instanceof WindowCreditManager) + || ((WindowCreditManager)_creditManager).getMessageCreditLimit() < 400 ) + { + _creditManager.restoreCredit(_deferredMessageCredit, _deferredSizeCredit); + _deferredMessageCredit = 0; + _deferredSizeCredit = 0l; + } + } public FlowCreditManager_0_10 getCreditManager() { @@ -804,6 +924,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void flush() throws AMQException { + flushCreditState(true); _queue.flushSubscription(this); stop(); } @@ -947,4 +1068,9 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return (LogSubject) this; } + + public void flushBatched() + { + _session.getConnection().flush(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java index e18b453db3..00f0c9f0f1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java @@ -24,6 +24,14 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CONNECTIO import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SOCKET_FORMAT; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.USER_FORMAT; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.JMException; + +import org.apache.qpid.server.management.ManagedObject; + +import org.apache.qpid.server.management.Managable; + import java.security.Principal; import java.text.MessageFormat; import java.util.ArrayList; @@ -55,7 +63,7 @@ import org.apache.qpid.transport.Method; import org.apache.qpid.transport.ProtocolEvent; import org.apache.qpid.transport.Session; -public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject, AuthorizationHolder +public class ServerConnection extends Connection implements Managable, AMQConnectionModel, LogSubject, AuthorizationHolder { private ConnectionConfig _config; private Runnable _onOpenTask; @@ -67,6 +75,10 @@ public class ServerConnection extends Connection implements AMQConnectionModel, private boolean _statisticsEnabled = false; private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final long _connectionId; + + private ServerConnectionMBean _mBean; + private VirtualHost _virtualHost; + private AtomicLong _lastIoTime = new AtomicLong(); public ServerConnection(final long connectionId) { @@ -133,9 +145,6 @@ public class ServerConnection extends Connection implements AMQConnectionModel, super.setConnectionDelegate(delegate); } - private VirtualHost _virtualHost; - - public VirtualHost getVirtualHost() { return _virtualHost; @@ -144,8 +153,18 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void setVirtualHost(VirtualHost virtualHost) { _virtualHost = virtualHost; - + initialiseStatistics(); + + try + { + _mBean = new ServerConnectionMBean(this); + _mBean.register(); + } + catch (JMException jme) + { + log.error("Unable to create mBean for ServerConnection",jme); + } } public void setConnectionConfig(final ConnectionConfig config) @@ -190,6 +209,7 @@ public class ServerConnection extends Connection implements AMQConnectionModel, @Override public void received(ProtocolEvent event) { + _lastIoTime.set(System.currentTimeMillis()); if (event.isConnectionControl()) { CurrentActor.set(_actor); @@ -260,6 +280,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void close(AMQConstant cause, String message) throws AMQException { closeSubscriptions(); + if (_mBean != null) + { + _mBean.unregister(); + _mBean = null; + } ConnectionCloseCode replyCode = ConnectionCloseCode.NORMAL; try { @@ -405,6 +430,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void closed() { closeSubscriptions(); + if (_mBean != null) + { + _mBean.unregister(); + _mBean = null; + } super.closed(); } @@ -416,4 +446,38 @@ public class ServerConnection extends Connection implements AMQConnectionModel, } } + public void receivedComplete() + { + for (Session ssn : getChannels()) + { + ((ServerSession)ssn).flushCreditState(); + } + } + + @Override + public ManagedObject getManagedObject() + { + return _mBean; + } + + @Override + public void send(ProtocolEvent event) + { + _lastIoTime.set(System.currentTimeMillis()); + super.send(event); + } + + public AtomicLong getLastIoTime() + { + return _lastIoTime; + } + + void checkForNotification() + { + int channelsCount = getSessionModels().size(); + if (_mBean != null && channelsCount >= getConnectionDelegate().getChannelMax()) + { + _mBean.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 8d6e0e0d80..66ed6f1e62 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -28,10 +28,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; - import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; - import org.apache.qpid.common.ServerPropertyNames; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.BrokerConfig; @@ -49,6 +47,7 @@ import org.apache.qpid.transport.ConnectionClose; import org.apache.qpid.transport.ConnectionCloseCode; import org.apache.qpid.transport.ConnectionOpen; import org.apache.qpid.transport.ConnectionOpenOk; +import org.apache.qpid.transport.ConnectionStartOk; import org.apache.qpid.transport.ConnectionTuneOk; import org.apache.qpid.transport.ServerDelegate; import org.apache.qpid.transport.Session; @@ -62,6 +61,8 @@ public class ServerConnectionDelegate extends ServerDelegate { private final String _localFQDN; private final IApplicationRegistry _appRegistry; + private int _maxNoOfChannels; + private Map<String,Object> _clientProperties; public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN) { @@ -77,6 +78,7 @@ public class ServerConnectionDelegate extends ServerDelegate _appRegistry = appRegistry; _localFQDN = localFQDN; + _maxNoOfChannels = ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); } private static Map<String, Object> createConnectionProperties(final BrokerConfig brokerConfig) @@ -154,7 +156,7 @@ public class ServerConnectionDelegate extends ServerDelegate public void connectionOpen(Connection conn, ConnectionOpen open) { final ServerConnection sconn = (ServerConnection) conn; - + VirtualHost vhost; String vhostName; if(open.hasVirtualHost()) @@ -222,7 +224,12 @@ public class ServerConnectionDelegate extends ServerDelegate @Override protected int getChannelMax() { - return ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); + return _maxNoOfChannels; + } + + protected void setChannelMax(int channelMax) + { + _maxNoOfChannels = channelMax; } @Override public void sessionDetach(Connection conn, SessionDetach dtc) @@ -253,6 +260,7 @@ public class ServerConnectionDelegate extends ServerDelegate { ssn = sessionAttachImpl(conn, atc); conn.registerSession(ssn); + ((ServerConnection)conn).checkForNotification(); } else { @@ -279,4 +287,16 @@ public class ServerConnectionDelegate extends ServerDelegate } return true; } + + @Override + public void connectionStartOk(Connection conn, ConnectionStartOk ok) + { + _clientProperties = ok.getClientProperties(); + super.connectionStartOk(conn, ok); + } + + public Map<String,Object> getClientProperties() + { + return _clientProperties; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java new file mode 100644 index 0000000000..17c7bed601 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java @@ -0,0 +1,264 @@ +/* + * + * 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.qpid.server.transport; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import javax.management.JMException; +import javax.management.NotCompliantMBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; + +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; +import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.AMQSessionModel; + +/** + * This MBean class implements the management interface. In order to make more attributes, operations and notifications + * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. + */ +@MBeanDescription("Management Bean for an AMQ Broker 0-10 Connection") +public class ServerConnectionMBean extends AbstractAMQManagedConnectionObject +{ + private final ServerConnection _serverConnection; + + @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-10 Connection") + protected ServerConnectionMBean(final ServerConnection serverConnection) throws NotCompliantMBeanException + { + super(serverConnection.getConfig().getAddress()); + _serverConnection = serverConnection; + } + + @Override + public ManagedObject getParentObject() + { + return _serverConnection.getVirtualHost().getManagedObject(); + } + + @Override + public String getClientId() + { + return _serverConnection.getClientId(); + } + + @Override + public String getAuthorizedId() + { + return _serverConnection.getAuthorizedPrincipal().getName(); + } + + @Override + public String getVersion() + { + return String.valueOf(_serverConnection.getConnectionDelegate().getClientProperties().get(ClientProperties.version.toString())); + } + + @Override + public String getRemoteAddress() + { + return _serverConnection.getConfig().getAddress(); + } + + @Override + public Date getLastIoTime() + { + return new Date(_serverConnection.getLastIoTime().longValue()); + } + + @Override + public Long getMaximumNumberOfChannels() + { + return (long) _serverConnection.getConnectionDelegate().getChannelMax(); + } + + @Override + public TabularData channels() throws IOException, JMException + { + final TabularDataSupport channelsList = new TabularDataSupport(_channelsType); + final List<AMQSessionModel> list = _serverConnection.getSessionModels(); + + for (final AMQSessionModel channel : list) + { + final ServerSession session = (ServerSession)channel; + Object[] itemValues = + { + session.getChannel(), + session.isTransactional(), + null, + session.getUnacknowledgedMessageCount(), + session.getBlocking() + }; + + final CompositeData channelData = new CompositeDataSupport(_channelType, + COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); + channelsList.put(channelData); + } + return channelsList; + } + + @Override + public void commitTransactions(int channelId) throws JMException + { + final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); + if (session == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + else if (session.isTransactional()) + { + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + try + { + session.commit(); + } + finally + { + CurrentActor.remove(); + } + } + } + + @Override + public void rollbackTransactions(int channelId) throws JMException + { + final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); + if (session == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + else if (session.isTransactional()) + { + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + try + { + session.rollback(); + } + finally + { + CurrentActor.remove(); + } + } + } + + @Override + public void closeConnection() throws Exception + { + _serverConnection.mgmtClose(); + } + + @Override + public void resetStatistics() throws Exception + { + _serverConnection.resetStatistics(); + } + + @Override + public double getPeakMessageDeliveryRate() + { + return _serverConnection.getMessageDeliveryStatistics().getPeak(); + } + + @Override + public double getPeakDataDeliveryRate() + { + return _serverConnection.getDataDeliveryStatistics().getPeak(); + } + + @Override + public double getMessageDeliveryRate() + { + return _serverConnection.getMessageDeliveryStatistics().getRate(); + } + + @Override + public double getDataDeliveryRate() + { + return _serverConnection.getDataDeliveryStatistics().getRate(); + } + + @Override + public long getTotalMessagesDelivered() + { + return _serverConnection.getMessageDeliveryStatistics().getTotal(); + } + + @Override + public long getTotalDataDelivered() + { + return _serverConnection.getDataDeliveryStatistics().getTotal(); + } + + @Override + public double getPeakMessageReceiptRate() + { + return _serverConnection.getMessageReceiptStatistics().getPeak(); + } + + @Override + public double getPeakDataReceiptRate() + { + return _serverConnection.getDataReceiptStatistics().getPeak(); + } + + @Override + public double getMessageReceiptRate() + { + return _serverConnection.getMessageReceiptStatistics().getRate(); + } + + @Override + public double getDataReceiptRate() + { + return _serverConnection.getDataReceiptStatistics().getRate(); + } + + @Override + public long getTotalMessagesReceived() + { + return _serverConnection.getMessageReceiptStatistics().getTotal(); + } + + @Override + public long getTotalDataReceived() + { + return _serverConnection.getDataReceiptStatistics().getTotal(); + } + + @Override + public boolean isStatisticsEnabled() + { + return _serverConnection.isStatisticsEnabled(); + } + + @Override + public void setStatisticsEnabled(boolean enabled) + { + _serverConnection.setStatisticsEnabled(enabled); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 429cc4cf66..23b77b1fd9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -37,9 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; - import javax.security.auth.Subject; - import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; @@ -93,7 +91,7 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi { public void onAccept(); - public void onRelease(); + public void onRelease(boolean setRedelivered); public void onReject(); @@ -230,13 +228,13 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi } - public void release(RangeSet ranges) + public void release(RangeSet ranges, final boolean setRedelivered) { dispositionChange(ranges, new MessageDispositionAction() { public void performAction(MessageDispositionChangeListener listener) { - listener.onRelease(); + listener.onRelease(setRedelivered); } }); } @@ -350,7 +348,7 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi _transaction.rollback(); for(MessageDispositionChangeListener listener : _messageDispositionListenerMap.values()) { - listener.onRelease(); + listener.onRelease(true); } _messageDispositionListenerMap.clear(); @@ -698,4 +696,23 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi unregister(subscription_0_10); } } + + public void flushCreditState() + { + final Collection<Subscription_0_10> subscriptions = getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subscriptions) + { + subscription_0_10.flushCreditState(false); + } + } + + public int getUnacknowledgedMessageCount() + { + return _messageDispositionListenerMap.size(); + } + + public boolean getBlocking() + { + return false; //TODO: Blocking not implemented on 0-10 yet. + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index c87919b478..a0dca53ed0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -100,6 +100,11 @@ public class ServerSessionDelegate extends SessionDelegate { private static final Logger LOGGER = Logger.getLogger(ServerSessionDelegate.class); + /** + * No-local queue argument is used to support the no-local feature of Durable Subscribers. + */ + private static final String QUEUE_ARGUMENT_NO_LOCAL = "no-local"; + public ServerSessionDelegate() { @@ -143,7 +148,7 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void messageRelease(Session session, MessageRelease method) { - ((ServerSession)session).release(method.getTransfers()); + ((ServerSession)session).release(method.getTransfers(), method.getSetRedelivered()); } @Override @@ -963,13 +968,10 @@ public class ServerSessionDelegate extends SessionDelegate if(method.hasArguments() && method.getArguments() != null) { - if(method.getArguments().containsKey("no-local")) + if(method.getArguments().containsKey(QUEUE_ARGUMENT_NO_LOCAL)) { - Object no_local = method.getArguments().get("no-local"); - if(no_local instanceof Boolean && ((Boolean)no_local)) - { - queue.setNoLocal(true); - } + Object noLocal = method.getArguments().get(QUEUE_ARGUMENT_NO_LOCAL); + queue.setNoLocal(convertBooleanValue(noLocal)); } } @@ -1079,6 +1081,30 @@ public class ServerSessionDelegate extends SessionDelegate } } + /** + * Converts a queue argument into a boolean value. For compatibility with the C++ + * and the clients, accepts with Boolean, String, or Number types. + * @param argValue argument value. + * + * @return true if set + */ + private boolean convertBooleanValue(Object argValue) + { + if(argValue instanceof Boolean && ((Boolean)argValue)) + { + return true; + } + else if (argValue instanceof String && Boolean.parseBoolean((String)argValue)) + { + return true; + } + else if (argValue instanceof Number && ((Number)argValue).intValue() != 0) + { + return true; + } + return false; + } + protected AMQQueue createQueue(final String queueName, final QueueDeclare body, VirtualHost virtualHost, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java index 5c4fe0aab8..248b3b2143 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java @@ -26,11 +26,13 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionConfiguration; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionQueueConfiguration; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.plugins.logging.SlowConsumerDetectionMessages; +import org.apache.qpid.slowconsumerdetection.policies.SlowConsumerPolicyPlugin; public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { @@ -56,7 +58,7 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin /** * Configures the slow consumer disconnect plugin by adding a listener to each exchange on this - * cirtual host to record all the configured queues in a cache for processing by the housekeeping + * virtual host to record all the configured queues in a cache for processing by the housekeeping * thread. * * @see Plugin#configure(ConfigurationPlugin) @@ -65,9 +67,10 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { _config = (SlowConsumerDetectionConfiguration) config; _listener = new ConfiguredQueueBindingListener(getVirtualHost().getName()); - for (AMQShortString exchangeName : getVirtualHost().getExchangeRegistry().getExchangeNames()) + final ExchangeRegistry exchangeRegistry = getVirtualHost().getExchangeRegistry(); + for (AMQShortString exchangeName : exchangeRegistry.getExchangeNames()) { - getVirtualHost().getExchangeRegistry().getExchange(exchangeName).addBindingListener(_listener); + exchangeRegistry.getExchange(exchangeName).addBindingListener(_listener); } } @@ -87,11 +90,21 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin try { - SlowConsumerDetectionQueueConfiguration config = + final SlowConsumerDetectionQueueConfiguration config = q.getConfiguration().getConfiguration(SlowConsumerDetectionQueueConfiguration.class.getName()); if (checkQueueStatus(q, config)) { - config.getPolicy().performPolicy(q); + final SlowConsumerPolicyPlugin policy = config.getPolicy(); + if (policy == null) + { + // We would only expect to see this during shutdown + _logger.warn("No slow consumer policy for queue " + q.getName()); + } + else + { + policy.performPolicy(q); + } + } } catch (Exception e) @@ -126,7 +139,10 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { if (config != null) { - _logger.info("Retrieved Queue(" + q.getName() + ") Config:" + config); + if (_logger.isInfoEnabled()) + { + _logger.info("Retrieved Queue(" + q.getName() + ") Config:" + config); + } int count = q.getMessageCount(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java deleted file mode 100644 index 2c0ceed80b..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java +++ /dev/null @@ -1,652 +0,0 @@ -/* - * 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.qpid.tools.messagestore; - -import org.apache.commons.cli.Option; -import org.apache.commons.cli.OptionBuilder; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.configuration.Configuration; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; -import org.apache.qpid.server.store.MemoryMessageStore; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.tools.messagestore.commands.Clear; -import org.apache.qpid.tools.messagestore.commands.Command; -import org.apache.qpid.tools.messagestore.commands.Copy; -import org.apache.qpid.tools.messagestore.commands.Dump; -import org.apache.qpid.tools.messagestore.commands.Help; -import org.apache.qpid.tools.messagestore.commands.List; -import org.apache.qpid.tools.messagestore.commands.Load; -import org.apache.qpid.tools.messagestore.commands.Quit; -import org.apache.qpid.tools.messagestore.commands.Select; -import org.apache.qpid.tools.messagestore.commands.Show; -import org.apache.qpid.tools.messagestore.commands.Move; -import org.apache.qpid.tools.messagestore.commands.Purge; -import org.apache.qpid.tools.utils.CommandParser; -import org.apache.qpid.tools.utils.Console; -import org.apache.qpid.tools.utils.SimpleCommandParser; -import org.apache.qpid.tools.utils.SimpleConsole; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.StringTokenizer; - -/** - * MessageStoreTool. - */ -public class MessageStoreTool -{ - /** Text outputted at the start of each console.*/ - private static final String BOILER_PLATE = "MessageStoreTool - for examining Persistent Qpid Broker MessageStore instances"; - - /** I/O Wrapper. */ - protected Console _console; - - /** Batch mode flag. */ - protected boolean _batchMode; - - /** Internal State object. */ - private State _state = new State(); - - private HashMap<String, Command> _commands = new HashMap<String, Command>(); - - /** SLF4J Logger. */ - private static Logger _devlog = LoggerFactory.getLogger(MessageStoreTool.class); - - /** Loaded configuration file. */ - private Configuration _config; - - /** Control used for main run loop. */ - private boolean _running = true; - private boolean _initialised = false; - - //---------------------------------------------------------------------------------------------------/ - - public static void main(String[] args) throws Configuration.InitException - { - - MessageStoreTool tool = new MessageStoreTool(args); - - tool.start(); - } - - - public MessageStoreTool(String[] args) throws Configuration.InitException - { - this(args, System.in, System.out); - } - - public MessageStoreTool(String[] args, InputStream in, OutputStream out) throws Configuration.InitException - { - BufferedReader consoleReader = new BufferedReader(new InputStreamReader(in)); - BufferedWriter consoleWriter = new BufferedWriter(new OutputStreamWriter(out)); - - Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(this))); - _batchMode = false; - - _console = new SimpleConsole(consoleWriter, consoleReader); - - _config = new Configuration(); - - setOptions(); - _config.processCommandline(args); - } - - - private void setOptions() - { - Option help = new Option("h", "help", false, "print this message"); - Option version = new Option("v", "version", false, "print the version information and exit"); - Option configFile = - OptionBuilder.withArgName("file").hasArg() - .withDescription("use given configuration file By " - + "default looks for a file named " - + Configuration.DEFAULT_CONFIG_FILE + " in " + Configuration.QPID_HOME) - .withLongOpt("config") - .create("c"); - - _config.setOption(help); - _config.setOption(version); - _config.setOption(configFile); - } - - public State getState() - { - return _state; - } - - public Map<String, Command> getCommands() - { - return _commands; - } - - public void setConfigurationFile(String configfile) throws Configuration.InitException - { - _config.loadConfig(new File(configfile)); - setup(); - } - - public Console getConsole() - { - return _console; - } - - public void setConsole(Console console) - { - _console = console; - } - - /** - * Simple ShutdownHook to cleanly shutdown the databases - */ - static class ShutdownHook implements Runnable - { - MessageStoreTool _tool; - - ShutdownHook(MessageStoreTool messageStoreTool) - { - _tool = messageStoreTool; - } - - public void run() - { - _tool.quit(); - } - } - - public void quit() - { - _running = false; - - if (_initialised) - { - ApplicationRegistry.remove(); - } - - _console.println("...exiting"); - - _console.close(); - } - - public void setBatchMode(boolean batchmode) - { - _batchMode = batchmode; - } - - /** - * Main loop - */ - protected void start() - { - setup(); - - if (!_initialised) - { - System.exit(1); - } - - _console.println(""); - - _console.println(BOILER_PLATE); - - runCLI(); - } - - private void setup() - { - loadDefaultVirtualHosts(); - - loadCommands(); - - _state.clearAll(); - } - - private void loadCommands() - { - _commands.clear(); - //todo Dynamically load the classes that exis in com.redhat.etp.qpid.commands - _commands.put("close", new Clear(this)); - _commands.put("copy", new Copy(this)); - _commands.put("dump", new Dump(this)); - _commands.put("help", new Help(this)); - _commands.put("list", new List(this)); - _commands.put("load", new Load(this)); - _commands.put("move", new Move(this)); - _commands.put("purge", new Purge(this)); - _commands.put("quit", new Quit(this)); - _commands.put("select", new Select(this)); - _commands.put("show", new Show(this)); - } - - private void loadDefaultVirtualHosts() - { - final File configFile = _config.getConfigFile(); - - loadVirtualHosts(configFile); - } - - private void loadVirtualHosts(File configFile) - { - - if (!configFile.exists()) - { - _devlog.error("Config file not found:" + configFile.getAbsolutePath()); - return; - } - else - { - _devlog.debug("using config file :" + configFile.getAbsolutePath()); - } - - try - { - ConfigurationFileApplicationRegistry registry = new ConfigurationFileApplicationRegistry(configFile); - - ApplicationRegistry.remove(); - - ApplicationRegistry.initialise(registry); - - checkMessageStores(); - _initialised = true; - } - catch (ConfigurationException e) - { - _console.println("Unable to load configuration due to configuration error: " + e.getMessage()); - e.printStackTrace(); - } - catch (Exception e) - { - _console.println("Unable to load configuration due to: " + e.getMessage()); - e.printStackTrace(); - } - - - } - - private void checkMessageStores() - { - Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts(); - - boolean warning = false; - for (VirtualHost vhost : vhosts) - { - if (vhost.getMessageStore() instanceof MemoryMessageStore) - { - _console.println("WARNING: Virtualhost '" + vhost.getName() + "' is using a MemoryMessageStore. " - + "Changes will not persist."); - warning = true; - } - } - - if (warning) - { - _console.println(""); - _console.println("Please ensure you are using the correct config file currently using '" - + _config.getConfigFile().getAbsolutePath() + "'"); - _console.println("New config file can be specifed by 'load <config file>' or -c on the commandline."); - _console.println(""); - } - } - - private void runCLI() - { - while (_running) - { - if (!_batchMode) - { - printPrompt(); - } - - String[] args = _console.readCommand(); - - while (args != null) - { - exec(args); - - if (_running) - { - if (!_batchMode) - { - printPrompt(); - } - - args = _console.readCommand(); - } - } - } - } - - private void printPrompt() - { - _console.print(prompt()); - } - - - /** - * Execute a script (batch mode). - * - * @param script The file script - */ - protected void runScripts(String script) - { - //Store Current State - boolean oldBatch = _batchMode; - CommandParser oldParser = _console.getCommandParser(); - setBatchMode(true); - - try - { - _devlog.debug("Running script '" + script + "'"); - - _console.setCommandParser(new SimpleCommandParser(new BufferedReader(new FileReader(script)))); - - start(); - } - catch (java.io.FileNotFoundException e) - { - _devlog.error("Script not found: '" + script + "' due to:" + e.getMessage()); - } - - //Restore previous state - _console.setCommandParser(oldParser); - setBatchMode(oldBatch); - } - - public String prompt() - { - String state = _state.toString(); - if (state != null && state.length() != 0) - { - return state + ":bdb$ "; - } - else - { - return "bdb$ "; - } - } - - /** - * Execute the command. - * - * @param args [command, arg0, arg1...]. - */ - protected void exec(String[] args) - { - // Comment lines start with a # - if (args.length == 0 || args[0].startsWith("#")) - { - return; - } - - final String command = args[0]; - - Command cmd = _commands.get(command); - - if (cmd == null) - { - _console.println("Command not understood: " + command); - } - else - { - cmd.execute(args); - } - } - - - /** - * Displays usage info. - */ - protected static void help() - { - System.out.println(BOILER_PLATE); - System.out.println("Usage: java " + MessageStoreTool.class + " [Options]"); - System.out.println(" [-c <broker config file>] : Defaults to \"$QPID_HOME/etc/config.xml\""); - } - - - /** - * This class is used to store the current state of the tool. - * - * This is then interrogated by the various commands to augment their behaviour. - * - * - */ - public static class State - { - private VirtualHost _vhost = null; - private AMQQueue _queue = null; - private Exchange _exchange = null; - private java.util.List<Long> _msgids = null; - - public State() - { - } - - public void setQueue(AMQQueue queue) - { - _queue = queue; - } - - public AMQQueue getQueue() - { - return _queue; - } - - public void setVhost(VirtualHost vhost) - { - _vhost = vhost; - } - - public VirtualHost getVhost() - { - return _vhost; - } - - public Exchange getExchange() - { - return _exchange; - } - - public void setExchange(Exchange exchange) - { - _exchange = exchange; - } - - public String toString() - { - StringBuilder status = new StringBuilder(); - - if (_vhost != null) - { - status.append(_vhost.getName()); - - if (_exchange != null) - { - status.append("["); - status.append(_exchange.getNameShortString()); - status.append("]"); - - if (_queue != null) - { - status.append("->'"); - status.append(_queue.getNameShortString()); - status.append("'"); - - if (_msgids != null) - { - status.append(printMessages()); - } - } - } - } - - return status.toString(); - } - - - public String printMessages() - { - StringBuilder sb = new StringBuilder(); - - Long previous = null; - - Long start = null; - for (Long id : _msgids) - { - if (previous != null) - { - if (id == previous + 1) - { - if (start == null) - { - start = previous; - } - } - else - { - if (start != null) - { - sb.append(","); - sb.append(start); - sb.append("-"); - sb.append(id); - start = null; - } - else - { - sb.append(","); - sb.append(previous); - } - } - } - - previous = id; - } - - if (start != null) - { - sb.append(","); - sb.append(start); - sb.append("-"); - sb.append(_msgids.get(_msgids.size() - 1)); - } - else - { - sb.append(","); - sb.append(previous); - } - - // surround list in () - sb.replace(0, 1, "("); - sb.append(")"); - return sb.toString(); - } - - public void clearAll() - { - _vhost = null; - clearExchange(); - } - - public void clearExchange() - { - _exchange = null; - clearQueue(); - } - - public void clearQueue() - { - _queue = null; - clearMessages(); - } - - public void clearMessages() - { - _msgids = null; - } - - /** - * A common location to provide parsing of the message id string - * utilised by a number of the commands. - * The String is comma separated list of ids that can be individual ids - * or a range (4-10) - * - * @param msgString string of msg ids to parse 1,2,4-10 - */ - public void setMessages(String msgString) - { - StringTokenizer tok = new StringTokenizer(msgString, ","); - - if (tok.hasMoreTokens()) - { - _msgids = new LinkedList<Long>(); - } - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - if (next.contains("-")) - { - Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); - Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); - - if (end >= start) - { - for (long l = start; l <= end; l++) - { - _msgids.add(l); - } - } - } - else - { - _msgids.add(Long.parseLong(next)); - } - } - - } - - public void setMessages(java.util.List<Long> msgids) - { - _msgids = msgids; - } - - public java.util.List<Long> getMessages() - { - return _msgids; - } - }//Class State - -}//Class MessageStoreTool diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java deleted file mode 100644 index 5444197cb4..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -public abstract class AbstractCommand implements Command -{ - protected Console _console; - protected MessageStoreTool _tool; - - public AbstractCommand(MessageStoreTool tool) - { - _console = tool.getConsole(); - _tool = tool; - } - - public void setOutput(Console out) - { - _console = out; - } - - protected void commandError(String message, String[] args) - { - _console.print(getCommand() + " : " + message); - - if (args != null) - { - for (int i = 1; i < args.length; i++) - { - _console.print(args[i]); - } - } - _console.println(""); - _console.println(help()); - } - - - public abstract String help(); - - public abstract String usage(); - - public abstract String getCommand(); - - - public abstract void execute(String... args); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java deleted file mode 100644 index b0006b3fe6..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Clear extends AbstractCommand -{ - public Clear(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Clears any selection."; - } - - public String usage() - { - return "clear [ all | virtualhost | exchange | queue | msgs ]"; - } - - public String getCommand() - { - return "clear"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length < 1) - { - doClose("all"); - } - else - { - doClose(args[1]); - } - } - - private void doClose(String type) - { - if (type.equals("virtualhost") - || type.equals("all")) - { - _tool.getState().clearAll(); - } - - if (type.equals("exchange")) - { - _tool.getState().clearExchange(); - } - - if (type.equals("queue")) - { - _tool.getState().clearQueue(); - } - - if (type.equals("msgs")) - { - _tool.getState().clearMessages(); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java deleted file mode 100644 index bfa775a34a..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.utils.Console; - -public interface Command -{ - public void setOutput(Console out); - - public String help(); - - public abstract String usage(); - - String getCommand(); - - public void execute(String... args); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java deleted file mode 100644 index 348c95572d..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.txn.ServerTransaction; -import org.apache.qpid.server.txn.LocalTransaction; - -public class Copy extends Move -{ - public Copy(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Copy messages between queues.";/*\n" + - "The currently selected message set will be copied to the specifed queue.\n" + - "Alternatively the values can be provided on the command line."; */ - } - - public String usage() - { - return "copy to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "copy"; - } - - protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) - { - ServerTransaction txn = new LocalTransaction(fromQueue.getVirtualHost().getTransactionLog()); - fromQueue.copyMessagesToAnotherQueue(start, end, toQueue.getNameShortString().toString(), txn); - txn.commit(); - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java deleted file mode 100644 index 8bb5d02b01..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.commons.codec.binary.Hex; -import org.apache.qpid.server.queue.QueueEntryImpl; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; -import java.util.List; - -public class Dump extends Show -{ - private static final int LINE_SIZE = 8; - private static final String DEFAULT_ENCODING = "utf-8"; - private static final boolean SPACE_BYTES = true; - private static final String BYTE_SPACER = " "; - private static final String NON_PRINTING_ASCII_CHAR = "?"; - - protected boolean _content = true; - - public Dump(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Dump selected message content. Default: show=content"; - } - - public String usage() - { - return getCommand() + " [show=[all],[msgheaders],[_amqHeaders],[routing],[content]] [id=<msgid e.g. 1,2,4-10>]"; - } - - public String getCommand() - { - return "dump"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("show=")) - { - _content = arg.contains("content") || arg.contains("all"); - } - } - - parseArgs(args); - } - - performShow(); - } - - - protected List<List> createMessageData(java.util.List<Long> msgids, List<QueueEntry> messages, boolean showHeaders, boolean showRouting, - boolean showMessageHeaders) - { - - List<List> display = new LinkedList<List>(); - - List<String> hex = new LinkedList<String>(); - List<String> ascii = new LinkedList<String>(); - display.add(hex); - display.add(ascii); - - for (QueueEntry entry : messages) - { - ServerMessage msg = entry.getMessage(); - if (!includeMsg(msg, msgids)) - { - continue; - } - - //Add divider between messages - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - // Show general message information - hex.add(Show.Columns.ID.name()); - ascii.add(msg.getMessageNumber().toString()); - - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - if (showRouting) - { - addShowInformation(hex, ascii, msg, "Routing Details", true, false, false); - } - if (showHeaders) - { - addShowInformation(hex, ascii, msg, "Headers", false, true, false); - } - if (showMessageHeaders) - { - addShowInformation(hex, ascii, msg, null, false, false, true); - } - - // Add Content Body section - hex.add("Content Body"); - ascii.add(""); - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - - final int messageSize = (int) msg.getSize(); - if (messageSize != 0) - { - hex.add("Hex"); - hex.add(Console.ROW_DIVIDER); - - - ascii.add("ASCII"); - ascii.add(Console.ROW_DIVIDER); - - java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(64 * 1024); - - int position = 0; - - while(position < messageSize) - { - - position += msg.getContent(buf, position); - buf.flip(); - //Duplicate so we don't destroy original data :) - java.nio.ByteBuffer hexBuffer = buf; - - java.nio.ByteBuffer charBuffer = hexBuffer.duplicate(); - - Hex hexencoder = new Hex(); - - while (hexBuffer.hasRemaining()) - { - byte[] line = new byte[LINE_SIZE]; - - int bufsize = hexBuffer.remaining(); - if (bufsize < LINE_SIZE) - { - hexBuffer.get(line, 0, bufsize); - } - else - { - bufsize = line.length; - hexBuffer.get(line); - } - - byte[] encoded = hexencoder.encode(line); - - try - { - String encStr = new String(encoded, 0, bufsize * 2, DEFAULT_ENCODING); - String hexLine = ""; - - int strLength = encStr.length(); - for (int c = 0; c < strLength; c++) - { - hexLine += encStr.charAt(c); - - if ((c & 1) == 1 && SPACE_BYTES) - { - hexLine += BYTE_SPACER; - } - } - - hex.add(hexLine); - } - catch (UnsupportedEncodingException e) - { - _console.println(e.getMessage()); - return null; - } - } - - while (charBuffer.hasRemaining()) - { - String asciiLine = ""; - - for (int pos = 0; pos < LINE_SIZE; pos++) - { - if (charBuffer.hasRemaining()) - { - byte ch = charBuffer.get(); - - if (isPrintable(ch)) - { - asciiLine += (char) ch; - } - else - { - asciiLine += NON_PRINTING_ASCII_CHAR; - } - - if (SPACE_BYTES) - { - asciiLine += BYTE_SPACER; - } - } - else - { - break; - } - } - - ascii.add(asciiLine); - } - buf.clear(); - } - } - else - { - List<String> result = new LinkedList<String>(); - - display.add(result); - result.add("No ContentBodies"); - } - } - - // if hex is empty then we have no data to display - if (hex.size() == 0) - { - return null; - } - - return display; - } - - private void addShowInformation(List<String> column1, List<String> column2, ServerMessage msg, - String title, boolean routing, boolean headers, boolean messageHeaders) - { - List<QueueEntry> single = new LinkedList<QueueEntry>(); - single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE)); - - List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders); - - //Reformat data - if (title != null) - { - column1.add(title); - column2.add(""); - column1.add(Console.ROW_DIVIDER); - column2.add(Console.ROW_DIVIDER); - } - - // look at all columns in the routing Data - for (List item : routingData) - { - // the item should be: - // Title - // *divider - // value - // otherwise we can't reason about the correct value - if (item.size() == 3) - { - //Filter out the columns we are not interested in. - - String columnName = item.get(0).toString(); - - if (!(columnName.equals(Show.Columns.ID.name()) - || columnName.equals(Show.Columns.Size.name()))) - { - column1.add(columnName); - column2.add(item.get(2).toString()); - } - } - } - column1.add(Console.ROW_DIVIDER); - column2.add(Console.ROW_DIVIDER); - } - - private boolean isPrintable(byte c) - { - return c > 31 && c < 127; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java deleted file mode 100644 index 0f9546541b..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.LinkedList; -import java.util.Map; - -public class Help extends AbstractCommand -{ - public Help(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Provides detailed help on commands."; - } - - public String getCommand() - { - return "help"; - } - - public String usage() - { - return "help [<command>]"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 1) - { - Command command = _tool.getCommands().get(args[1]); - if (command != null) - { - _console.println(command.help()); - _console.println("Usage:" + command.usage()); - } - else - { - commandError("Command not found: ", args); - } - } - else - { - java.util.List<java.util.List> data = new LinkedList<java.util.List>(); - - java.util.List<String> commandName = new LinkedList<String>(); - java.util.List<String> commandDescription = new LinkedList<String>(); - - data.add(commandName); - data.add(commandDescription); - - //Set up Headers - commandName.add("Command"); - commandDescription.add("Description"); - - commandName.add(Console.ROW_DIVIDER); - commandDescription.add(Console.ROW_DIVIDER); - - //Add current Commands with descriptions - Map<String, Command> commands = _tool.getCommands(); - - for (Command command : commands.values()) - { - commandName.add(command.getCommand()); - commandDescription.add(command.help()); - } - - _console.printMap("Available Commands", data); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java deleted file mode 100644 index 3c4a0c8fac..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.Collection; -import java.util.LinkedList; - -public class List extends AbstractCommand -{ - - public List(MessageStoreTool tool) - { - super(tool); - } - - public void setOutput(Console out) - { - _console = out; - } - - public String help() - { - return "list available items."; - } - - public String usage() - { - return "list queues [<exchange>] | exchanges | bindings [<exchange>] | all"; - } - - public String getCommand() - { - return "list"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 1) - { - if ((args[1].equals("exchanges")) - || (args[1].equals("queues")) - || (args[1].equals("bindings")) - || (args[1].equals("all"))) - { - if (args.length == 2) - { - doList(args[1]); - } - else if (args.length == 3) - { - doList(args[1], args[2]); - } - } - else - { - commandError("Unknown options. ", args); - } - } - else if (args.length < 2) - { - doList("all"); - } - else - { - doList(args[1]); - } - } - - private void doList(String... listItem) - { - if (_tool.getState().getVhost() == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - listVirtualHosts(); - return; - } - - VirtualHost vhost = _tool.getState().getVhost(); - - java.util.List<String> data = null; - - if (listItem[0].equals("queues")) - { - if (listItem.length > 1) - { - data = listQueues(vhost, new AMQShortString(listItem[1])); - } - else - { - Exchange exchange = _tool.getState().getExchange(); - data = listQueues(vhost, exchange); - } - } - - if (listItem[0].equals("exchanges")) - { - data = listExchanges(vhost); - } - - if (listItem[0].equals("bindings")) - { - - if (listItem.length > 1) - { - data = listBindings(vhost, new AMQShortString(listItem[1])); - } - else - { - Exchange exchange = _tool.getState().getExchange(); - - data = listBindings(vhost, exchange); - } - } - - if (data != null) - { - if (data.size() == 1) - { - _console.println("No '" + listItem[0] + "' to display,"); - } - else - { - _console.displayList(true, data.toArray(new String[0])); - } - } - - - if (listItem[0].equals("all")) - { - - boolean displayed = false; - Exchange exchange = _tool.getState().getExchange(); - - //Do the display here for each one so that they are pretty printed - data = listQueues(vhost, exchange); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - - if (exchange == null) - { - data = listExchanges(vhost); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - } - - data = listBindings(vhost, exchange); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - - if (!displayed) - { - _console.println("Nothing to list"); - } - } - } - - private void listVirtualHosts() - { - Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance() - .getVirtualHostRegistry().getVirtualHosts(); - - String[] data = new String[vhosts.size() + 1]; - - data[0] = "Available VirtualHosts"; - - int index = 1; - for (VirtualHost vhost : vhosts) - { - data[index] = vhost.getName(); - index++; - } - - _console.displayList(true, data); - } - - private java.util.List<String> listBindings(VirtualHost vhost, AMQShortString exchangeName) - { - return listBindings(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); - } - - private java.util.List<String> listBindings(VirtualHost vhost, Exchange exchange) - { - Collection<AMQShortString> queues = vhost.getQueueRegistry().getQueueNames(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Current Bindings"); - - for (AMQShortString queue : queues) - { - if (exchange != null) - { - if (exchange.isBound(queue)) - { - data.add(queue.toString()); - } - } - else - { - data.add(queue.toString()); - } - } - - return data; - } - - private java.util.List<String> listExchanges(VirtualHost vhost) - { - Collection<AMQShortString> queues = vhost.getExchangeRegistry().getExchangeNames(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Available Exchanges"); - - for (AMQShortString queue : queues) - { - data.add(queue.toString()); - } - - return data; - } - - private java.util.List<String> listQueues(VirtualHost vhost, AMQShortString exchangeName) - { - return listQueues(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); - } - - private java.util.List<String> listQueues(VirtualHost vhost, Exchange exchange) - { - Collection<AMQQueue> queues = vhost.getQueueRegistry().getQueues(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Available Queues"); - - for (AMQQueue queue : queues) - { - if (exchange != null) - { - if (exchange.isBound(queue)) - { - data.add(queue.getNameShortString().toString()); - } - } - else - { - data.add(queue.getNameShortString().toString()); - } - } - - if (exchange != null) - { - if (queues.size() == 1) - { - return null; - } - } - - return data; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java deleted file mode 100644 index 244a311c30..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.configuration.Configuration; -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Load extends AbstractCommand -{ - public Load(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Loads specified broker configuration file."; - } - - public String usage() - { - return "load <configuration file>"; - } - - public String getCommand() - { - return "load"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 2) - { - _console.print("load " + args[1] + ": additional options not understood:"); - for (int i = 2; i < args.length; i++) - { - _console.print(args[i] + " "); - } - _console.println(""); - } - else if (args.length < 2) - { - _console.println("Enter Configuration file."); - String input = _console.readln(); - if (input != null) - { - doLoad(input); - } - else - { - _console.println("Did not recognise config file."); - } - } - else - { - doLoad(args[1]); - } - } - - private void doLoad(String configfile) - { - _console.println("Loading Configuration:" + configfile); - - try - { - _tool.setConfigurationFile(configfile); - } - catch (Configuration.InitException e) - { - _console.println("Unable to open config file due to: '" + e.getMessage() + "'"); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java deleted file mode 100644 index 615f6ec1c2..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.txn.ServerTransaction; -import org.apache.qpid.server.txn.LocalTransaction; -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -import java.util.LinkedList; -import java.util.List; - -public class Move extends AbstractCommand -{ - - public Move(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Move messages between queues.";/*\n" + - "The currently selected message set will be moved to the specifed queue.\n" + - "Alternatively the values can be provided on the command line.";*/ - } - - public String usage() - { - return "move to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "move"; - } - - public void execute(String... args) - { - AMQQueue toQueue = null; - AMQQueue fromQueue = _tool.getState().getQueue(); - java.util.List<Long> msgids = _tool.getState().getMessages(); - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("to=")) - { - String queueName = arg.substring(arg.indexOf("=") + 1); - toQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - } - - if (arg.startsWith("from=")) - { - String queueName = arg.substring(arg.indexOf("=") + 1); - fromQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - } - - if (arg.startsWith("msgids=")) - { - String msgidStr = arg.substring(arg.indexOf("=") + 1); - - // Record the current message selection - java.util.List<Long> currentIDs = _tool.getState().getMessages(); - - // Use the ToolState class to perform the messasge parsing - _tool.getState().setMessages(msgidStr); - msgids = _tool.getState().getMessages(); - - // Reset the original selection of messages - _tool.getState().setMessages(currentIDs); - } - } - } - - if (!checkRequirements(fromQueue, toQueue, msgids)) - { - return; - } - - processIDs(fromQueue, toQueue, msgids); - } - - private void processIDs(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids) - { - Long previous = null; - Long start = null; - - if (msgids == null) - { - msgids = allMessageIDs(fromQueue); - } - - if (msgids == null || msgids.size() == 0) - { - _console.println("No Messages to move."); - return; - } - - for (long id : msgids) - { - if (previous != null) - { - if (id == previous + 1) - { - if (start == null) - { - start = previous; - } - } - else - { - if (start != null) - { - //move a range of ids - doCommand(fromQueue, start, id, toQueue); - start = null; - } - else - { - //move a single id - doCommand(fromQueue, id, id, toQueue); - } - } - } - - previous = id; - } - - if (start != null) - { - //move a range of ids - doCommand(fromQueue, start, previous, toQueue); - } - } - - private List<Long> allMessageIDs(AMQQueue fromQueue) - { - List<Long> ids = new LinkedList<Long>(); - - if (fromQueue != null) - { - List<QueueEntry> messages = fromQueue.getMessagesOnTheQueue(); - if (messages != null) - { - for (QueueEntry msg : messages) - { - ids.add(msg.getMessage().getMessageNumber()); - } - } - } - - return ids; - } - - protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, List<Long> msgids) - { - if (toQueue == null) - { - _console.println("Destination queue not specifed."); - _console.println(usage()); - return false; - } - - if (fromQueue == null) - { - _console.println("Source queue not specifed."); - _console.println(usage()); - return false; - } - - return true; - } - - protected void doCommand(AMQQueue fromQueue, long start, long id, AMQQueue toQueue) - { - ServerTransaction txn = new LocalTransaction(fromQueue.getVirtualHost().getTransactionLog()); - fromQueue.moveMessagesToAnotherQueue(start, id, toQueue.getNameShortString().toString(), txn); - txn.commit(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java deleted file mode 100644 index 8df4afa2db..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.server.queue.AMQQueue; - -public class Purge extends Move -{ - public Purge(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Purge messages from a queue.\n" + - "The currently selected message set will be purged from the specifed queue.\n" + - "Alternatively the values can be provided on the command line."; - } - - public String usage() - { - return "purge from=<queue> [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "purge"; - } - - - protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids) - { - if (fromQueue == null) - { - _console.println("Source queue not specifed."); - _console.println(usage()); - return false; - } - - return true; - } - - protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) - { - fromQueue.removeMessagesFromQueue(start, end); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java deleted file mode 100644 index a81bc07c38..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Quit extends AbstractCommand -{ - public Quit(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Quit the tool."; - } - - public String usage() - { - return "quit"; - } - - public String getCommand() - { - return "quit"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals("quit"); - - _tool.quit(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java deleted file mode 100644 index ff59568374..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -import java.util.LinkedList; -import java.util.StringTokenizer; - -public class Select extends AbstractCommand -{ - - public Select(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Perform a selection."; - } - - public String usage() - { - return "select virtualhost <name> |exchange <name> |queue <name> | msg id=<msgids eg. 1,2,4-10>"; - } - - public String getCommand() - { - return "select"; - } - - public void execute(String... args) - { - assert args.length > 2; - assert args[0].equals("select"); - - if (args.length < 3) - { - if (args[1].equals("show")) - { - doSelect(args[1], null); - } - else - { - _console.print("select : unknown command:"); - _console.println(help()); - } - } - else - { - if (args[1].equals("virtualhost") - || args[1].equals("vhost") - || args[1].equals("exchange") - || args[1].equals("queue") - || args[1].equals("msg") - ) - { - doSelect(args[1], args[2]); - } - else - { - _console.println(help()); - } - } - } - - private void doSelect(String type, String item) - { - if (type.equals("virtualhost")) - { - - VirtualHost vhost = ApplicationRegistry.getInstance() - .getVirtualHostRegistry().getVirtualHost(item); - - if (vhost == null) - { - _console.println("Virtualhost '" + item + "' not found."); - } - else - { - _tool.getState().setVhost(vhost); - } - } - - if (type.equals("exchange")) - { - - VirtualHost vhost = _tool.getState().getVhost(); - - if (vhost == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - return; - } - - - Exchange exchange = vhost.getExchangeRegistry().getExchange(new AMQShortString(item)); - - if (exchange == null) - { - _console.println("Exchange '" + item + "' not found."); - } - else - { - _tool.getState().setExchange(exchange); - } - - if (_tool.getState().getQueue() != null) - { - if (!exchange.isBound(_tool.getState().getQueue())) - { - _tool.getState().setQueue(null); - } - } - } - - if (type.equals("queue")) - { - VirtualHost vhost = _tool.getState().getVhost(); - - if (vhost == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - return; - } - - AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(item)); - - if (queue == null) - { - _console.println("Queue '" + item + "' not found."); - } - else - { - _tool.getState().setQueue(queue); - - if (_tool.getState().getExchange() == null) - { - for (AMQShortString exchangeName : vhost.getExchangeRegistry().getExchangeNames()) - { - Exchange exchange = vhost.getExchangeRegistry().getExchange(exchangeName); - if (exchange.isBound(queue)) - { - _tool.getState().setExchange(exchange); - break; - } - } - } - - //remove the message selection - _tool.getState().setMessages((java.util.List<Long>) null); - } - } - - if (type.equals("msg")) - { - if (item.startsWith("id=")) - { - StringTokenizer tok = new StringTokenizer(item.substring(item.indexOf("=") + 1), ","); - - java.util.List<Long> msgids = null; - - if (tok.hasMoreTokens()) - { - msgids = new LinkedList<Long>(); - } - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - if (next.contains("-")) - { - Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); - Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); - - if (end >= start) - { - for (long l = start; l <= end; l++) - { - msgids.add(l); - } - } - } - else - { - msgids.add(Long.parseLong(next)); - } - } - - _tool.getState().setMessages(msgids); - } - - } - - if (type.equals("show")) - { - _console.println(_tool.getState().toString()); - if (_tool.getState().getMessages() != null) - { - _console.print("Msgs:"); - for (Long l : _tool.getState().getMessages()) - { - _console.print(" " + l); - } - _console.println(""); - } - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java deleted file mode 100644 index 806e161bbc..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * 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.qpid.tools.messagestore.commands; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.message.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.LinkedList; -import java.util.List; - -public class Show extends AbstractCommand -{ - protected boolean _amqHeaders = false; - protected boolean _routing = false; - protected boolean _msgHeaders = false; - - public Show(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Shows the messages headers."; - } - - public String usage() - { - return getCommand() + " [show=[all],[msgheaders],[amqheaders],[routing]] [id=<msgid e.g. 1,2,4-10>]"; - } - - public String getCommand() - { - return "show"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length < 2) - { - parseArgs("all"); - } - else - { - parseArgs(args); - } - - performShow(); - } - - protected void parseArgs(String... args) - { - List<Long> msgids = null; - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("show=")) - { - _msgHeaders = arg.contains("msgheaders") || arg.contains("all"); - _amqHeaders = arg.contains("amqheaders") || arg.contains("all"); - _routing = arg.contains("routing") || arg.contains("all"); - } - - if (arg.startsWith("id=")) - { - _tool.getState().setMessages(msgids); - } - }//for args - }// if args > 2 - } - - protected void performShow() - { - if (_tool.getState().getVhost() == null) - { - _console.println("No Virtualhost selected. 'DuSelect' a Virtualhost first."); - return; - } - - AMQQueue _queue = _tool.getState().getQueue(); - - List<Long> msgids = _tool.getState().getMessages(); - - if (_queue != null) - { - List<QueueEntry> messages = _queue.getMessagesOnTheQueue(); - if (messages == null || messages.size() == 0) - { - _console.println("No messages on queue"); - return; - } - - List<List> data = createMessageData(msgids, messages, _amqHeaders, _routing, _msgHeaders); - if (data != null) - { - _console.printMap(null, data); - } - else - { - String message = "No data to display."; - if (msgids != null) - { - message += " Is message selection correct? " + _tool.getState().printMessages(); - } - _console.println(message); - } - - } - else - { - _console.println("No Queue specified to show."); - } - } - - /** - * Create the list data for display from the messages. - * - * @param msgids The list of message ids to display - * @param messages A list of messages to format and display. - * @param showHeaders should the header info be shown - * @param showRouting show the routing info be shown - * @param showMessageHeaders show the msg headers be shown - * @return the formated data lists for printing - */ - protected List<List> createMessageData(List<Long> msgids, List<QueueEntry> messages, boolean showHeaders, boolean showRouting, - boolean showMessageHeaders) - { - - // Currenly exposed message properties -// //Printing the content Body -// msg.getContentBodyIterator(); -// //Print the Headers -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppIdAsString(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getClusterId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getContentType(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getCorrelationId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getDeliveryMode(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getEncoding(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getExpiration(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getMessageNumber(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPriority(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPropertyFlags(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getReplyTo(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getTimestamp(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getType(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getUserId(); -// -// //Print out all the property names -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders().getPropertyNames(); -// -// msg.getMessageNumber(); -// msg.getSize(); -// msg.getArrivalTime(); - -// msg.getDeliveredSubscription(); -// msg.getDeliveredToConsumer(); -// msg.getMessageHandle(); -// msg.getMessageNumber(); -// msg.getMessagePublishInfo(); -// msg.getPublisher(); - -// msg.getStoreContext(); -// msg.isAllContentReceived(); -// msg.isPersistent(); -// msg.isRedelivered(); -// msg.isRejectedBy(); -// msg.isTaken(); - - //Header setup - - List<List> data = new LinkedList<List>(); - - List<String> id = new LinkedList<String>(); - data.add(id); - id.add(Columns.ID.name()); - id.add(Console.ROW_DIVIDER); - - List<String> exchange = new LinkedList<String>(); - List<String> routingkey = new LinkedList<String>(); - List<String> immediate = new LinkedList<String>(); - List<String> mandatory = new LinkedList<String>(); - if (showRouting) - { - data.add(exchange); - exchange.add(Columns.Exchange.name()); - exchange.add(Console.ROW_DIVIDER); - - data.add(routingkey); - routingkey.add(Columns.RoutingKey.name()); - routingkey.add(Console.ROW_DIVIDER); - - data.add(immediate); - immediate.add(Columns.isImmediate.name()); - immediate.add(Console.ROW_DIVIDER); - - data.add(mandatory); - mandatory.add(Columns.isMandatory.name()); - mandatory.add(Console.ROW_DIVIDER); - } - - List<String> size = new LinkedList<String>(); - List<String> appid = new LinkedList<String>(); - List<String> clusterid = new LinkedList<String>(); - List<String> contenttype = new LinkedList<String>(); - List<String> correlationid = new LinkedList<String>(); - List<String> deliverymode = new LinkedList<String>(); - List<String> encoding = new LinkedList<String>(); - List<String> arrival = new LinkedList<String>(); - List<String> expiration = new LinkedList<String>(); - List<String> priority = new LinkedList<String>(); - List<String> propertyflag = new LinkedList<String>(); - List<String> replyto = new LinkedList<String>(); - List<String> timestamp = new LinkedList<String>(); - List<String> type = new LinkedList<String>(); - List<String> userid = new LinkedList<String>(); - List<String> ispersitent = new LinkedList<String>(); - List<String> isredelivered = new LinkedList<String>(); - List<String> isdelivered = new LinkedList<String>(); - - data.add(size); - size.add(Columns.Size.name()); - size.add(Console.ROW_DIVIDER); - - if (showHeaders) - { - data.add(ispersitent); - ispersitent.add(Columns.isPersistent.name()); - ispersitent.add(Console.ROW_DIVIDER); - - data.add(isredelivered); - isredelivered.add(Columns.isRedelivered.name()); - isredelivered.add(Console.ROW_DIVIDER); - - data.add(isdelivered); - isdelivered.add(Columns.isDelivered.name()); - isdelivered.add(Console.ROW_DIVIDER); - - data.add(appid); - appid.add(Columns.App_ID.name()); - appid.add(Console.ROW_DIVIDER); - - data.add(clusterid); - clusterid.add(Columns.Cluster_ID.name()); - clusterid.add(Console.ROW_DIVIDER); - - data.add(contenttype); - contenttype.add(Columns.Content_Type.name()); - contenttype.add(Console.ROW_DIVIDER); - - data.add(correlationid); - correlationid.add(Columns.Correlation_ID.name()); - correlationid.add(Console.ROW_DIVIDER); - - data.add(deliverymode); - deliverymode.add(Columns.Delivery_Mode.name()); - deliverymode.add(Console.ROW_DIVIDER); - - data.add(encoding); - encoding.add(Columns.Encoding.name()); - encoding.add(Console.ROW_DIVIDER); - - data.add(arrival); - expiration.add(Columns.Arrival.name()); - expiration.add(Console.ROW_DIVIDER); - - data.add(expiration); - expiration.add(Columns.Expiration.name()); - expiration.add(Console.ROW_DIVIDER); - - data.add(priority); - priority.add(Columns.Priority.name()); - priority.add(Console.ROW_DIVIDER); - - data.add(propertyflag); - propertyflag.add(Columns.Property_Flag.name()); - propertyflag.add(Console.ROW_DIVIDER); - - data.add(replyto); - replyto.add(Columns.ReplyTo.name()); - replyto.add(Console.ROW_DIVIDER); - - data.add(timestamp); - timestamp.add(Columns.Timestamp.name()); - timestamp.add(Console.ROW_DIVIDER); - - data.add(type); - type.add(Columns.Type.name()); - type.add(Console.ROW_DIVIDER); - - data.add(userid); - userid.add(Columns.UserID.name()); - userid.add(Console.ROW_DIVIDER); - } - - List<String> msgHeaders = new LinkedList<String>(); - if (showMessageHeaders) - { - data.add(msgHeaders); - msgHeaders.add(Columns.MsgHeaders.name()); - msgHeaders.add(Console.ROW_DIVIDER); - } - - //Add create the table of data - for (QueueEntry entry : messages) - { - ServerMessage msg = entry.getMessage(); - if (!includeMsg(msg, msgids)) - { - continue; - } - - id.add(msg.getMessageNumber().toString()); - - size.add("" + msg.getSize()); - - arrival.add("" + msg.getArrivalTime()); - - ispersitent.add(msg.isPersistent() ? "true" : "false"); - - - isredelivered.add(entry.isRedelivered() ? "true" : "false"); - - isdelivered.add(entry.getDeliveredToConsumer() ? "true" : "false"); - -// msg.getMessageHandle(); - - BasicContentHeaderProperties headers = null; - - try - { - if(msg instanceof AMQMessage) - { - headers = ((BasicContentHeaderProperties) ((AMQMessage)msg).getContentHeaderBody().getProperties()); - } - } - catch (AMQException e) - { - //ignore -// commandError("Unable to read properties for message: " + e.getMessage(), null); - } - - if (headers != null) - { - String appidS = headers.getAppIdAsString(); - appid.add(appidS == null ? "null" : appidS); - - String clusterS = headers.getClusterIdAsString(); - clusterid.add(clusterS == null ? "null" : clusterS); - - String contentS = headers.getContentTypeAsString(); - contenttype.add(contentS == null ? "null" : contentS); - - String correlationS = headers.getCorrelationIdAsString(); - correlationid.add(correlationS == null ? "null" : correlationS); - - deliverymode.add("" + headers.getDeliveryMode()); - - AMQShortString encodeSS = headers.getEncoding(); - encoding.add(encodeSS == null ? "null" : encodeSS.toString()); - - expiration.add("" + headers.getExpiration()); - - FieldTable headerFT = headers.getHeaders(); - msgHeaders.add(headerFT == null ? "none" : "" + headerFT.toString()); - - priority.add("" + headers.getPriority()); - propertyflag.add("" + headers.getPropertyFlags()); - - AMQShortString replytoSS = headers.getReplyTo(); - replyto.add(replytoSS == null ? "null" : replytoSS.toString()); - - timestamp.add("" + headers.getTimestamp()); - - AMQShortString typeSS = headers.getType(); - type.add(typeSS == null ? "null" : typeSS.toString()); - - AMQShortString useridSS = headers.getUserId(); - userid.add(useridSS == null ? "null" : useridSS.toString()); - - MessagePublishInfo info = null; - try - { - if(msg instanceof AMQMessage) - { - info = ((AMQMessage)msg).getMessagePublishInfo(); - } - - } - catch (AMQException e) - { - //ignore - } - - if (info != null) - { - AMQShortString exchangeSS = info.getExchange(); - exchange.add(exchangeSS == null ? "null" : exchangeSS.toString()); - - AMQShortString routingkeySS = info.getRoutingKey(); - routingkey.add(routingkeySS == null ? "null" : routingkeySS.toString()); - - immediate.add(info.isImmediate() ? "true" : "false"); - mandatory.add(info.isMandatory() ? "true" : "false"); - } - -// msg.getPublisher(); -- only used in clustering -// msg.getStoreContext(); -// msg.isAllContentReceived(); - - }// if headers!=null - -// need to access internal map and do lookups. -// msg.isTaken(); -// msg.getDeliveredSubscription(); -// msg.isRejectedBy(); - - } - - // if id only had the header and the divider in it then we have no data to display - if (id.size() == 2) - { - return null; - } - return data; - } - - protected boolean includeMsg(ServerMessage msg, List<Long> msgids) - { - if (msgids == null) - { - return true; - } - - Long msgid = msg.getMessageNumber(); - - boolean found = false; - - if (msgids != null) - { - //check msgid is in msgids - for (Long l : msgids) - { - if (l.equals(msgid)) - { - found = true; - break; - } - } - } - return found; - } - - public enum Columns - { - ID, - Size, - Exchange, - RoutingKey, - isImmediate, - isMandatory, - isPersistent, - isRedelivered, - isDelivered, - App_ID, - Cluster_ID, - Content_Type, - Correlation_ID, - Delivery_Mode, - Encoding, - Arrival, - Expiration, - Priority, - Property_Flag, - ReplyTo, - Timestamp, - Type, - UserID, - MsgHeaders - } -} - - diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java index c27c52eb8e..bfcdbe7460 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java @@ -60,9 +60,6 @@ public class Passwd private static void output(String user, byte[] encoded) throws IOException { - -// File passwdFile = new File("qpid.passwd"); - PrintStream ps = new PrintStream(System.out); user += ":"; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java deleted file mode 100644 index 986fea32cc..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.qpid.tools.utils; - -public interface CommandParser -{ - /** - * If there is more than one command received on the last parse request. - * - * Subsequent calls to parse will utilise this input rather than reading new data from the input source - * @return boolean - */ - boolean more(); - - /** - * True if the currently parsed command has been requested as a background operation - * - * @return boolean - */ - boolean isBackground(); - - /** - * Parses user commands, and groups tokens in the - * String[] format that all Java main's love. - * - * If more than one command is provided in one input line then the more() method will return true. - * A subsequent call to parse() will continue to parse that input line before reading new input. - * - * @return <code>input</code> split in args[] format; null if eof. - * @throws java.io.IOException if there is a problem reading from the input stream - */ - String[] parse() throws java.io.IOException; -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java deleted file mode 100644 index cf457d1ea5..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.qpid.tools.utils; - -import java.util.List; - -public interface Console -{ - public enum CellFormat - { - CENTRED, LEFT, RIGHT - } - - public static String ROW_DIVIDER = "*divider"; - - public void print(String... message); - - public void println(String... message); - - public String readln(); - - /** - * Reads and parses the command line. - * - * - * @return The next command or null - */ - public String[] readCommand(); - - public CommandParser getCommandParser(); - - public void setCommandParser(CommandParser parser); - - /** - * - * Prints the list of String nicely. - * - * +-------------+ - * | Heading | - * +-------------+ - * | Item 1 | - * | Item 2 | - * | Item 3 | - * +-------------+ - * - * @param hasTitle should list[0] be used as a heading - * @param list The list of Strings to display - */ - public void displayList(boolean hasTitle, String... list); - - /** - * - * Prints the list of String nicely. - * - * +----------------------------+ - * | Heading | - * +----------------------------+ - * | title | title | .. - * +----------------------------+ - * | Item 2 | value 2 | .. - * +----------------------------+ (*divider) - * | Item 3 | value 2 | .. - * +----------------------------+ - * - * @param title The title to display if any - * @param entries the entries to display in a map. - */ - void printMap(String title, List<List> entries); - - - public void close(); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java deleted file mode 100644 index 09444ccdd7..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.qpid.tools.utils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.StringTokenizer; - -public class SimpleCommandParser implements CommandParser -{ - private static final String COMMAND_SEPERATOR = ";"; - - /** Input source of commands */ - protected BufferedReader _reader; - - /** The next list of commands from the command line */ - private StringBuilder _nextCommand = null; - - public SimpleCommandParser(BufferedReader reader) - { - _reader = reader; - } - - public boolean more() - { - return _nextCommand != null; - } - - public boolean isBackground() - { - return false; - } - - public String[] parse() throws IOException - { - String[] commands = null; - - String input = null; - - if (_nextCommand == null) - { - input = _reader.readLine(); - } - else - { - input = _nextCommand.toString(); - _nextCommand = null; - } - - if (input == null) - { - return null; - } - - StringTokenizer tok = new StringTokenizer(input, " "); - - int tokenCount = tok.countTokens(); - int index = 0; - - if (tokenCount > 0) - { - commands = new String[tokenCount]; - boolean commandComplete = false; - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - - if (next.equals(COMMAND_SEPERATOR)) - { - commandComplete = true; - _nextCommand = new StringBuilder(); - continue; - } - - if (commandComplete) - { - _nextCommand.append(next); - _nextCommand.append(" "); - } - else - { - commands[index] = next; - index++; - } - } - - } - - //Reduce the String[] if not all the tokens were used in this command. - // i.e. there is more than one command on the line. - if (index != tokenCount) - { - String[] shortCommands = new String[index]; - System.arraycopy(commands, 0, shortCommands, 0, index); - return shortCommands; - } - else - { - return commands; - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java deleted file mode 100644 index 2791a39f92..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * 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.qpid.tools.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -public class SimpleConsole implements Console -{ - /** SLF4J Logger. */ - private static Logger _devlog = LoggerFactory.getLogger(SimpleConsole.class); - - /** Console Writer. */ - protected BufferedWriter _consoleWriter; - - /** Console Reader. */ - protected BufferedReader _consoleReader; - - /** Parser for command-line input. */ - protected CommandParser _parser; - - public SimpleConsole(BufferedWriter writer, BufferedReader reader) - { - _consoleWriter = writer; - _consoleReader = reader; - _parser = new SimpleCommandParser(_consoleReader); - } - - public void print(String... message) - { - try - { - for (String s : message) - { - _consoleWriter.write(s); - } - _consoleWriter.flush(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occurred whilst trying to write:" + Arrays.asList(message)); - } - - } - - public void println(String... message) - { - print(message); - print(System.getProperty("line.separator")); - } - - - public String readln() - { - try - { - return _consoleReader.readLine(); - } - catch (IOException e) - { - _devlog.debug("Unable to read input due to:" + e.getMessage()); - return null; - } - } - - public String[] readCommand() - { - try - { - return _parser.parse(); - } - catch (IOException e) - { - _devlog.error("Error reading command:" + e.getMessage()); - return new String[0]; - } - } - - public CommandParser getCommandParser() - { - return _parser; - } - - public void setCommandParser(CommandParser parser) - { - _parser = parser; - } - - public void displayList(boolean hasTitle, String... list) - { - java.util.List<java.util.List> data = new LinkedList<List>(); - - java.util.List<String> values = new LinkedList<String>(); - - data.add(values); - - for (String value : list) - { - values.add(value); - } - - if (hasTitle) - { - values.add(1, "*divider"); - } - - printMap(null, data); - } - - /** - * - * Prints the list of String nicely. - * - * +----------------------------+ - * | Heading | - * +----------------------------+ - * | title | title | .. - * +----------------------------+ - * | Item 2 | value 2 | .. - * | Item 3 | value 2 | .. - * +----------------------------+ - * - * @param title The title to display if any - * @param entries the entries to display in a map. - */ - public void printMap(String title, java.util.List<java.util.List> entries) - { - try - { - int columns = entries.size(); - - int[] columnWidth = new int[columns]; - - // calculate row count - int rowMax = 0; - - //the longest item - int itemMax = 0; - - for (int i = 0; i < columns; i++) - { - int columnIRowMax = entries.get(i).size(); - - if (columnIRowMax > rowMax) - { - rowMax = columnIRowMax; - } - for (Object values : entries.get(i)) - { - if (values.toString().equals(Console.ROW_DIVIDER)) - { - continue; - } - - int itemLength = values.toString().length(); - - //note for single width - if (itemLength > itemMax) - { - itemMax = itemLength; - } - - //note for mulit width - if (itemLength > columnWidth[i]) - { - columnWidth[i] = itemLength; - } - - } - } - - int tableWidth = 0; - - - for (int i = 0; i < columns; i++) - { - // plus 2 for the space padding - columnWidth[i] += 2; - } - for (int size : columnWidth) - { - tableWidth += size; - } - tableWidth += (columns - 1); - - if (title != null) - { - if (title.length() > tableWidth) - { - tableWidth = title.length(); - } - - printCellRow("+", "-", tableWidth); - - printCell(CellFormat.CENTRED, "|", tableWidth, " " + title + " ", 0); - _consoleWriter.newLine(); - - } - - //put top line | or bottom of title - printCellRow("+", "-", tableWidth); - - //print the table data - int row = 0; - - for (; row < rowMax; row++) - { - for (int i = 0; i < columns; i++) - { - java.util.List columnData = entries.get(i); - - String value; - // does this column have a value for this row - if (columnData.size() > row) - { - value = " " + columnData.get(row).toString() + " "; - } - else - { - value = " "; - } - - if (i == 0 && value.equals(" " + Console.ROW_DIVIDER + " ")) - { - printCellRow("+", "-", tableWidth); - //move on to the next row - break; - } - else - { - printCell(CellFormat.LEFT, "|", columnWidth[i], value, i); - } - - // if it is the last row then do a new line. - if (i == columns - 1) - { - _consoleWriter.newLine(); - } - } - } - - printCellRow("+", "-", tableWidth); - - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to write."); - } - } - - public void close() - { - - try - { - _consoleReader.close(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to close reader."); - } - - try - { - - _consoleWriter.close(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to close writer."); - } - - } - - private void printCell(CellFormat format, String edge, int cellWidth, String cell, int column) throws IOException - { - int pad = cellWidth - cell.length(); - - if (column == 0) - { - _consoleWriter.write(edge); - } - - switch (format) - { - case CENTRED: - printPad(" ", pad / 2); - break; - case RIGHT: - printPad(" ", pad); - break; - } - - _consoleWriter.write(cell); - - - switch (format) - { - case CENTRED: - // if pad isn't even put the extra one on the right - if (pad % 2 == 0) - { - printPad(" ", pad / 2); - } - else - { - printPad(" ", (pad / 2) + 1); - } - break; - case LEFT: - printPad(" ", pad); - break; - } - - - _consoleWriter.write(edge); - - } - - private void printCellRow(String edge, String mid, int cellWidth) throws IOException - { - _consoleWriter.write(edge); - - printPad(mid, cellWidth); - - _consoleWriter.write(edge); - _consoleWriter.newLine(); - } - - private void printPad(String padChar, int count) throws IOException - { - for (int i = 0; i < count; i++) - { - _consoleWriter.write(padChar); - } - } - - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java index 6c135e8ba7..7d128f2bc5 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java @@ -20,18 +20,31 @@ */ package org.apache.qpid.server; -import junit.framework.TestCase; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.queue.AMQPriorityQueue; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHostImpl; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.test.utils.QpidTestCase; -public class AMQBrokerManagerMBeanTest extends InternalBrokerBaseCase +public class AMQBrokerManagerMBeanTest extends QpidTestCase { private QueueRegistry _queueRegistry; private ExchangeRegistry _exchangeRegistry; @@ -81,14 +94,100 @@ public class AMQBrokerManagerMBeanTest extends InternalBrokerBaseCase assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null); } + public void testCreateNewQueueBindsToDefaultExchange() throws Exception + { + String queueName = "testQueue_" + System.currentTimeMillis(); + + ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); + ExchangeRegistry exReg = _vHost.getExchangeRegistry(); + Exchange defaultExchange = exReg.getDefaultExchange(); + + mbean.createNewQueue(queueName, "test", false); + assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) != null); + + assertTrue("New queue should be bound to default exchange", defaultExchange.isBound(new AMQShortString(queueName))); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument does cause the + * maximum delivery count to be set on the Queue. + */ + public void testCreateNewQueueWithMaximumDeliveryCount() throws Exception + { + final Map<String,Object> args = new HashMap<String, Object>(); + args.put(AMQQueueFactory.X_QPID_MAXIMUM_DELIVERY_COUNT, 5); + + final AMQShortString queueName = new AMQShortString("testCreateNewQueueWithMaximumDeliveryCount"); + + final QueueRegistry qReg = _vHost.getQueueRegistry(); + + assertNull("The queue should not yet exist", qReg.getQueue(queueName)); + + final ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); + mbean.createNewQueue(queueName.asString(), "test", false, args); + + final AMQQueue createdQueue = qReg.getQueue(queueName); + assertNotNull("The queue was not registered as expected", createdQueue); + assertEquals("Unexpected maximum delivery count", 5, createdQueue.getMaximumDeliveryCount()); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_PRIORITIES} argument prompts creation of + * a Priority Queue. + */ + public void testCreatePriorityQueue() throws Exception + { + int numPriorities = 7; + Map<String,Object> args = new HashMap<String, Object>(); + args.put(AMQQueueFactory.X_QPID_PRIORITIES, numPriorities); + + AMQShortString queueName = new AMQShortString("testCreatePriorityQueue"); + + QueueRegistry qReg = _vHost.getQueueRegistry(); + + assertNull("The queue should not yet exist", qReg.getQueue(queueName)); + + ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); + mbean.createNewQueue(queueName.asString(), "test", false, args); + + AMQQueue queue = qReg.getQueue(queueName); + assertEquals("Queue is not a priorty queue", AMQPriorityQueue.class, queue.getClass()); + assertEquals("Number of priorities supported was not as expected", numPriorities, ((AMQPriorityQueue)queue).getPriorities()); + } + @Override public void setUp() throws Exception { super.setUp(); + + CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); + + XMLConfiguration configXml = new XMLConfiguration(); + configXml.addProperty("virtualhosts.virtualhost(-1).name", "test"); + configXml.addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName()); + + ServerConfiguration configuration = new ServerConfiguration(configXml); + + ApplicationRegistry registry = new TestApplicationRegistry(configuration); + ApplicationRegistry.initialise(registry); + registry.getVirtualHostRegistry().setDefaultVirtualHostName("test"); + IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); _vHost = appRegistry.getVirtualHostRegistry().getVirtualHost("test"); _queueRegistry = _vHost.getQueueRegistry(); _exchangeRegistry = _vHost.getExchangeRegistry(); } + @Override + public void tearDown() throws Exception + { + try + { + super.tearDown(); + } + finally + { + ApplicationRegistry.remove(); + } + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java index 9b0ae82b84..eea8e173f4 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.server; import java.util.EnumSet; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java new file mode 100644 index 0000000000..73e0bc5d27 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java @@ -0,0 +1,151 @@ +package org.apache.qpid.server.configuration; + +import java.util.UUID; + +public class MockConnectionConfig implements ConnectionConfig +{ + + public MockConnectionConfig(UUID _id, ConnectionConfigType _configType, + ConfiguredObject<ConnectionConfigType, ConnectionConfig> _parent, boolean _durable, + long _createTime, VirtualHostConfig _virtualHost, String _address, Boolean _incoming, + Boolean _systemConnection, Boolean _federationLink, String _authId, String _remoteProcessName, + Integer _remotePID, Integer _remoteParentPID, ConfigStore _configStore, Boolean _shadow) + { + super(); + this._id = _id; + this._configType = _configType; + this._parent = _parent; + this._durable = _durable; + this._createTime = _createTime; + this._virtualHost = _virtualHost; + this._address = _address; + this._incoming = _incoming; + this._systemConnection = _systemConnection; + this._federationLink = _federationLink; + this._authId = _authId; + this._remoteProcessName = _remoteProcessName; + this._remotePID = _remotePID; + this._remoteParentPID = _remoteParentPID; + this._configStore = _configStore; + this._shadow = _shadow; + } + + private UUID _id; + private ConnectionConfigType _configType; + private ConfiguredObject<ConnectionConfigType, ConnectionConfig> _parent; + private boolean _durable; + private long _createTime; + private VirtualHostConfig _virtualHost; + private String _address; + private Boolean _incoming; + private Boolean _systemConnection; + private Boolean _federationLink; + private String _authId; + private String _remoteProcessName; + private Integer _remotePID; + private Integer _remoteParentPID; + private ConfigStore _configStore; + private Boolean _shadow; + + @Override + public UUID getId() + { + return _id; + } + + @Override + public ConnectionConfigType getConfigType() + { + return _configType; + } + + @Override + public ConfiguredObject<ConnectionConfigType, ConnectionConfig> getParent() + { + return _parent; + } + + @Override + public boolean isDurable() + { + return _durable; + } + + @Override + public long getCreateTime() + { + return _createTime; + } + + @Override + public VirtualHostConfig getVirtualHost() + { + return _virtualHost; + } + + @Override + public String getAddress() + { + return _address; + } + + @Override + public Boolean isIncoming() + { + return _incoming; + } + + @Override + public Boolean isSystemConnection() + { + return _systemConnection; + } + + @Override + public Boolean isFederationLink() + { + return _federationLink; + } + + @Override + public String getAuthId() + { + return _authId; + } + + @Override + public String getRemoteProcessName() + { + return _remoteProcessName; + } + + @Override + public Integer getRemotePID() + { + return _remotePID; + } + + @Override + public Integer getRemoteParentPID() + { + return _remoteParentPID; + } + + @Override + public ConfigStore getConfigStore() + { + return _configStore; + } + + @Override + public Boolean isShadow() + { + return _shadow; + } + + @Override + public void mgmtClose() + { + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java index d2f2ae5eea..e1a5e7d338 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java @@ -24,6 +24,8 @@ import junit.framework.TestCase; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.TestApplicationRegistry; public class QueueConfigurationTest extends TestCase { @@ -43,11 +45,71 @@ public class QueueConfigurationTest extends TestCase fullEnv.setProperty("queues.maximumMessageSize", 1); fullEnv.setProperty("queues.maximumMessageCount", 1); fullEnv.setProperty("queues.minimumAlertRepeatGap", 1); + fullEnv.setProperty("queues.deadLetterQueues", true); + fullEnv.setProperty("queues.maximumDeliveryCount", 5); _fullHostConf = new VirtualHostConfiguration("test", fullEnv); } + public void testMaxDeliveryCount() throws Exception + { + try + { + ApplicationRegistry registry = new TestApplicationRegistry(new ServerConfiguration(_env)); + ApplicationRegistry.initialise(registry); + + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf); + assertEquals("Unexpected default server configuration for max delivery count ", 0, qConf.getMaxDeliveryCount()); + + // Check explicit value + VirtualHostConfiguration vhostConfig = overrideConfiguration("maximumDeliveryCount", 7); + qConf = new QueueConfiguration("test", vhostConfig); + assertEquals("Unexpected host configuration for max delivery count", 7, qConf.getMaxDeliveryCount()); + + // Check inherited value + qConf = new QueueConfiguration("test", _fullHostConf); + assertEquals("Unexpected queue configuration for max delivery count", 5, qConf.getMaxDeliveryCount()); + + } + finally + { + ApplicationRegistry.remove(); + } + } + + /** + * Tests that the default setting for DLQ configuration is disabled, and verifies that it can be overridden + * at a broker or virtualhost level. + * @throws Exception + */ + public void testIsDeadLetterQueueEnabled() throws Exception + { + try + { + ApplicationRegistry registry = new TestApplicationRegistry(new ServerConfiguration(_env)); + ApplicationRegistry.initialise(registry); + + // Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf); + assertFalse("Unexpected queue configuration for dead letter enabled attribute", qConf.isDeadLetterQueueEnabled()); + + // Check explicit value + VirtualHostConfiguration vhostConfig = overrideConfiguration("deadLetterQueues", true); + qConf = new QueueConfiguration("test", vhostConfig); + assertTrue("Unexpected queue configuration for dead letter enabled attribute", qConf.isDeadLetterQueueEnabled()); + + // Check inherited value + qConf = new QueueConfiguration("test", _fullHostConf); + assertTrue("Unexpected queue configuration for dead letter enabled attribute", qConf.isDeadLetterQueueEnabled()); + } + finally + { + ApplicationRegistry.remove(); + } + } + public void testGetMaximumMessageAge() throws ConfigurationException { // Check default value @@ -129,7 +191,19 @@ public class QueueConfigurationTest extends TestCase assertEquals(1, qConf.getMinimumAlertRepeatGap()); } - private VirtualHostConfiguration overrideConfiguration(String property, int value) + public void testSortQueueConfiguration() throws ConfigurationException + { + //Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf); + assertNull(qConf.getQueueSortKey()); + + // Check explicit value + final VirtualHostConfiguration vhostConfig = overrideConfiguration("sortKey", "test-sort-key"); + qConf = new QueueConfiguration("test", vhostConfig); + assertEquals("test-sort-key", qConf.getQueueSortKey()); + } + + private VirtualHostConfiguration overrideConfiguration(String property, Object value) throws ConfigurationException { PropertiesConfiguration queueConfig = new PropertiesConfiguration(); @@ -141,5 +215,4 @@ public class QueueConfigurationTest extends TestCase return new VirtualHostConfiguration("test", config); } - } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 9afd2a45a9..7739f9976e 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -25,6 +25,7 @@ import static org.apache.qpid.transport.ConnectionSettings.WILDCARD_ADDRESS; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; import java.util.Locale; import org.apache.commons.configuration.ConfigurationException; @@ -303,6 +304,18 @@ public class ServerConfigurationTest extends QpidTestCase assertEquals(false, _serverConfig.getManagementEnabled()); } + public void testGetManagementRightsInferAllAccess() throws Exception + { + _serverConfig.initialise(); + + //check default + assertTrue("default should be true", _serverConfig.getManagementRightsInferAllAccess()); + + //update it + _config.setProperty("management.managementRightsInferAllAccess", "false"); + assertFalse("New value should be false", _serverConfig.getManagementRightsInferAllAccess()); + } + public void testGetHeartBeatDelay() throws ConfigurationException { // Check default @@ -1447,4 +1460,117 @@ public class ServerConfigurationTest extends QpidTestCase ce.getMessage()); } } + + public void testMaxDeliveryCountDefault() throws Exception + { + final ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(0, serverConfig.getMaxDeliveryCount()); + } + + public void testMaxDeliveryCount() throws Exception + { + _config.setProperty("maximumDeliveryCount", 5); + final ServerConfiguration serverConfig = new ServerConfiguration(_config); + assertEquals(5, serverConfig.getMaxDeliveryCount()); + } + + /** + * Test XML configuration file correctly enables dead letter queues + */ + public void testDeadLetterQueueConfigurationFile() throws Exception + { + // Write config + File xml = File.createTempFile(getClass().getName(), "xml"); + xml.deleteOnExit(); + FileWriter config = new FileWriter(xml); + config.write("<broker>\n"); + writeSecurity(config); + config.write("<deadLetterQueues>true</deadLetterQueues>\n"); + config.write("<virtualhosts>\n"); + config.write("<virtualhost>\n"); + config.write("<name>test</name>\n"); + config.write("<test>\n"); + config.write("<queues>\n"); + config.write("<deadLetterQueues>false</deadLetterQueues>\n"); + config.write("<queue>\n"); + config.write("<name>biggles</name>\n"); + config.write("<biggles>\n"); + config.write("<deadLetterQueues>true</deadLetterQueues>\n"); + config.write("</biggles>\n"); + config.write("</queue>\n"); + config.write("<queue>\n"); + config.write("<name>beetle</name>\n"); + config.write("<beetle />\n"); + config.write("</queue>\n"); + config.write("</queues>\n"); + config.write("</test>\n"); + config.write("</virtualhost>\n"); + config.write("<virtualhost>\n"); + config.write("<name>extra</name>\n"); + config.write("<extra>\n"); + config.write("<queues>\n"); + config.write("<queue>\n"); + config.write("<name>r2d2</name>\n"); + config.write("<r2d2>\n"); + config.write("<deadLetterQueues>false</deadLetterQueues>\n"); + config.write("</r2d2>\n"); + config.write("</queue>\n"); + config.write("<queue>\n"); + config.write("<name>c3p0</name>\n"); + config.write("<c3p0 />\n"); + config.write("</queue>\n"); + config.write("</queues>\n"); + config.write("</extra>\n"); + config.write("</virtualhost>\n"); + config.write("</virtualhosts>\n"); + config.write("</broker>\n"); + config.close(); + + // Load config + ApplicationRegistry.remove(); + ApplicationRegistry registry = new ConfigurationFileApplicationRegistry(xml); + ApplicationRegistry.initialise(registry); + ServerConfiguration serverConfiguration = ApplicationRegistry.getInstance().getConfiguration(); + + VirtualHostConfiguration test = serverConfiguration.getVirtualHostConfig("test"); + assertNotNull("Host 'test' is not found", test); + VirtualHostConfiguration extra = serverConfiguration.getVirtualHostConfig("extra"); + assertNotNull("Host 'extra' is not found", test); + + QueueConfiguration biggles = test.getQueueConfiguration("biggles"); + QueueConfiguration beetle = test.getQueueConfiguration("beetle"); + QueueConfiguration r2d2 = extra.getQueueConfiguration("r2d2"); + QueueConfiguration c3p0 = extra.getQueueConfiguration("c3p0"); + + // Validate config + assertTrue("Broker DLQ should be configured as enabled", serverConfiguration.isDeadLetterQueueEnabled()); + assertFalse("Test vhost DLQ should be configured as disabled", test.isDeadLetterQueueEnabled()); + assertTrue("Extra vhost DLQ should be enabled, using broker default", extra.isDeadLetterQueueEnabled()); + assertTrue("Biggles queue DLQ should be configured as enabled", biggles.isDeadLetterQueueEnabled()); + assertFalse("Beetle queue DLQ should be disabled, using test vhost default", beetle.isDeadLetterQueueEnabled()); + assertFalse("R2D2 queue DLQ should be configured as disabled", r2d2.isDeadLetterQueueEnabled()); + assertTrue("C3P0 queue DLQ should be enabled, using broker default", c3p0.isDeadLetterQueueEnabled()); + } + + /** + * Convenience method to output required security preamble for broker config + */ + private void writeSecurity(Writer out) throws Exception + { + out.write("\t<management><enabled>false</enabled></management>\n"); + out.write("\t<security>\n"); + out.write("\t\t<pd-auth-manager>\n"); + out.write("\t\t\t<principal-database>\n"); + out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n"); + out.write("\t\t\t\t<attributes>\n"); + out.write("\t\t\t\t\t<attribute>\n"); + out.write("\t\t\t\t\t\t<name>passwordFile</name>\n"); + out.write("\t\t\t\t\t\t<value>/dev/null</value>\n"); + out.write("\t\t\t\t\t</attribute>\n"); + out.write("\t\t\t\t</attributes>\n"); + out.write("\t\t\t</principal-database>\n"); + out.write("\t\t\t<jmx-access>/dev/null</jmx-access>\n"); + out.write("\t\t</pd-auth-manager>\n"); + out.write("\t</security>\n"); + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java index b133d53ac5..f6cd397217 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.configuration; import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; @@ -34,19 +33,6 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase { @Override - public void setUp() throws Exception - { - super.setUp(); - // Set the default configuration items - getConfigXml().clear(); - getConfigXml().addProperty("virtualhosts.virtualhost(-1).name", "test"); - getConfigXml().addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName()); - - getConfigXml().addProperty("virtualhosts.virtualhost.name", getName()); - getConfigXml().addProperty("virtualhosts.virtualhost."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); - } - - @Override public void createBroker() { // Prevent auto broker startup @@ -134,6 +120,88 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase assertEquals(3, bTest.getMaximumMessageAge()); } + public void testMaxDeliveryCount() throws Exception + { + // Set up vhosts and queues + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues.maximumDeliveryCount", 5); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues(-1).queue(-1).name", "biggles"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues.queue.biggles.maximumDeliveryCount", 4); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues(-1).queue(-1).name", "beetle"); + + // Start the broker now. + super.createBroker(); + + // Get vhosts + VirtualHost test = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName()); + + // Enabled specifically + assertEquals("Test vhost MDC was configured as enabled", 5 ,test.getConfiguration().getMaxDeliveryCount()); + + // Enabled by test vhost default + assertEquals("beetle queue DLQ was configured as enabled", test.getConfiguration().getMaxDeliveryCount(), test.getConfiguration().getQueueConfiguration("beetle").getMaxDeliveryCount()); + + // Disabled specifically + assertEquals("Biggles queue DLQ was configured as disabled", 4, test.getConfiguration().getQueueConfiguration("biggles").getMaxDeliveryCount()); + } + + /** + * Tests the full set of configuration options for enabling DLQs in the broker configuration. + */ + public void testIsDeadLetterQueueEnabled() throws Exception + { + // Set up vhosts and queues + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues.deadLetterQueues", "true"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues(-1).queue(-1).name", "biggles"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues.queue.biggles.deadLetterQueues", "false"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + ".queues(-1).queue(-1).name", "beetle"); + + + getConfigXml().addProperty("virtualhosts.virtualhost.name", getName() + "Extra"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "r2d2"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues.queue.r2d2.deadLetterQueues", "true"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "c3p0"); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.store.class", TestableMemoryMessageStore.class.getName()); + + // Start the broker now. + super.createBroker(); + + // Get vhosts + VirtualHost test = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName()); + VirtualHost extra = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(getName() + "Extra"); + + // Enabled specifically + assertTrue("Test vhost DLQ was configured as enabled", test.getConfiguration().isDeadLetterQueueEnabled()); + assertTrue("r2d2 queue DLQ was configured as enabled", extra.getConfiguration().getQueueConfiguration("r2d2").isDeadLetterQueueEnabled()); + + // Enabled by test vhost default + assertTrue("beetle queue DLQ was configured as enabled", test.getConfiguration().getQueueConfiguration("beetle").isDeadLetterQueueEnabled()); + + // Disabled specifically + assertFalse("Biggles queue DLQ was configured as disabled", test.getConfiguration().getQueueConfiguration("biggles").isDeadLetterQueueEnabled()); + + // Using broker default of disabled + assertFalse("Extra vhost DLQ disabled, using broker default", extra.getConfiguration().isDeadLetterQueueEnabled()); + assertFalse("c3p0 queue DLQ was configured as disabled", extra.getConfiguration().getQueueConfiguration("c3p0").isDeadLetterQueueEnabled()); + + // Get queues + AMQQueue biggles = test.getQueueRegistry().getQueue(new AMQShortString("biggles")); + AMQQueue beetle = test.getQueueRegistry().getQueue(new AMQShortString("beetle")); + AMQQueue r2d2 = extra.getQueueRegistry().getQueue(new AMQShortString("r2d2")); + AMQQueue c3p0 = extra.getQueueRegistry().getQueue(new AMQShortString("c3p0")); + + // Disabled specifically for this queue, overriding virtualhost setting + assertNull("Biggles queue should not have alt exchange as DLQ should be configured as disabled: " + biggles.getAlternateExchange(), biggles.getAlternateExchange()); + + // Enabled for all queues on the virtualhost + assertNotNull("Beetle queue should have an alt exchange as DLQ should be enabled, using test vhost default", beetle.getAlternateExchange()); + + // Enabled specifically for this queue, overriding the default broker setting of disabled + assertNotNull("R2D2 queue should have an alt exchange as DLQ should be configured as enabled", r2d2.getAlternateExchange()); + + // Disabled by the default broker setting + assertNull("C3PO queue should not have an alt exchange as DLQ should be disabled, using broker default", c3p0.getAlternateExchange()); + } + /** * Test that the house keeping pool sizes is correctly processed * @@ -173,7 +241,7 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase vhost.getHouseKeepingTaskCount()); // Currently the two are tasks: - // ExpiredMessageTask from VirtualHost + // ExpiredMessageTask from VirtualHost // UpdateTask from the QMF ManagementExchange } @@ -214,7 +282,7 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase { getConfigXml().addProperty("virtualhosts.virtualhost.testSecurityAuthenticationNameRejected.security.authentication.name", "testdb"); - + try { super.createBroker(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java index 886396acb8..a0a29cf734 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -20,9 +20,17 @@ */ package org.apache.qpid.server.exchange; -import junit.framework.TestCase; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -52,17 +60,6 @@ import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.util.InternalBrokerBaseCase; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase { private static final Logger _log = Logger.getLogger(AbstractHeadersExchangeTestBase.class); @@ -488,6 +485,32 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase { return false; } + + public QueueEntry getNextNode() + { + return null; + } + + public QueueEntry getNextValidEntry() + { + return null; + } + + @Override + public int getDeliveryCount() + { + return 0; + } + + @Override + public void incrementDeliveryCount() + { + } + + @Override + public void decrementDeliveryCount() + { + } }; if(action != null) diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java index 71e92b5294..68021f0b07 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java @@ -20,8 +20,7 @@ */ package org.apache.qpid.server.exchange; -import junit.framework.TestCase; - +import org.apache.commons.lang.ArrayUtils; import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.queue.AMQQueue; @@ -34,9 +33,11 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; +import javax.management.JMException; +import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.TabularData; import java.util.ArrayList; -import java.util.Collections; +import java.util.Iterator; /** * Unit test class for testing different Exchange MBean operations @@ -47,10 +48,20 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase private QueueRegistry _queueRegistry; private VirtualHost _virtualHost; - /** - * Test for direct exchange mbean - * @throws Exception - */ + public void testGeneralProperties() throws Exception + { + DirectExchange exchange = new DirectExchange(); + exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + // test general exchange properties + assertEquals("Unexpected exchange name", "amq.direct", mbean.getName()); + assertEquals("Unexpected exchange type", "direct", mbean.getExchangeType()); + assertEquals("Unexpected ticket number", Integer.valueOf(0), mbean.getTicketNo()); + assertFalse("Unexpected durable flag", mbean.isDurable()); + assertTrue("Unexpected auto delete flag", mbean.isAutoDelete()); + } public void testDirectExchangeMBean() throws Exception { @@ -65,20 +76,8 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase TabularData data = mbean.bindings(); ArrayList<Object> list = new ArrayList<Object>(data.values()); assertTrue(list.size() == 2); - - // test general exchange properties - assertEquals(mbean.getName(), "amq.direct"); - assertEquals(mbean.getExchangeType(), "direct"); - assertTrue(mbean.getTicketNo() == 0); - assertTrue(!mbean.isDurable()); - assertTrue(mbean.isAutoDelete()); } - /** - * Test for "topic" exchange mbean - * @throws Exception - */ - public void testTopicExchangeMBean() throws Exception { TopicExchange exchange = new TopicExchange(); @@ -92,42 +91,81 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase TabularData data = mbean.bindings(); ArrayList<Object> list = new ArrayList<Object>(data.values()); assertTrue(list.size() == 2); + } - // test general exchange properties - assertEquals(mbean.getName(), "amq.topic"); - assertEquals(mbean.getExchangeType(), "topic"); - assertTrue(mbean.getTicketNo() == 0); - assertTrue(!mbean.isDurable()); - assertTrue(mbean.isAutoDelete()); + public void testHeadersExchangeMBean() throws Exception + { + HeadersExchange exchange = new HeadersExchange(); + exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,key1=binding1,key2=binding2"); + + TabularData data = mbean.bindings(); + ArrayList<Object> list = new ArrayList<Object>(data.values()); + assertEquals("Unexpected number of bindings", 1, list.size()); + + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) data.values().iterator(); + CompositeDataSupport row = rowItr.next(); + assertBinding(1, _queue.getName(), new String[]{"x-match=any","key1=binding1","key2=binding2"}, row); } /** - * Test for "Headers" exchange mbean - * @throws Exception + * Included to ensure 0-10 Specification compliance: + * 2.3.1.4 "the field in the bind arguments has no value and a field of the same name is present in the message headers */ - - public void testHeadersExchangeMBean() throws Exception + public void testHeadersExchangeMBeanMatchPropertyNoValue() throws Exception { HeadersExchange exchange = new HeadersExchange(); exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; - mbean.createNewBinding(_queue.getNameShortString().toString(), "key1=binding1,key2=binding2"); - mbean.createNewBinding(_queue.getNameShortString().toString(), "key3=binding3"); + mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,key4,key5="); TabularData data = mbean.bindings(); ArrayList<Object> list = new ArrayList<Object>(data.values()); - assertTrue(list.size() == 2); + assertEquals("Unexpected number of bindings", 1, list.size()); - // test general exchange properties - assertEquals(mbean.getName(), "amq.match"); - assertEquals(mbean.getExchangeType(), "headers"); - assertTrue(mbean.getTicketNo() == 0); - assertTrue(!mbean.isDurable()); - assertTrue(mbean.isAutoDelete()); + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) data.values().iterator(); + CompositeDataSupport row = rowItr.next(); + assertBinding(1, _queue.getName(), new String[]{"x-match=any","key4=","key5="}, row); } - + + public void testInvalidHeaderBindingMalformed() throws Exception + { + HeadersExchange exchange = new HeadersExchange(); + exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + try + { + mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,=value4"); + fail("Exception not thrown"); + } + catch (JMException jme) + { + //pass + } + } + + private void assertBinding(final int expectedBindingNo, final String expectedQueueName, final String[] expectedBindingArray, + final CompositeDataSupport row) + { + final Number bindingNumber = (Number) row.get(ManagedExchange.HDR_BINDING_NUMBER); + final String queueName = (String) row.get(ManagedExchange.HDR_QUEUE_NAME); + final String[] bindings = (String[]) row.get(ManagedExchange.HDR_QUEUE_BINDINGS); + assertEquals("Unexpected binding number", expectedBindingNo, bindingNumber); + assertEquals("Unexpected queue name", expectedQueueName, queueName); + assertEquals("Unexpected no of bindings", expectedBindingArray.length, bindings.length); + for(String binding : bindings) + { + assertTrue("Expected binding not found: " + binding, ArrayUtils.contains(expectedBindingArray, binding)); + } + } + /** * Test adding bindings and removing them from the default exchange via JMX. * <p> diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/flow/WindowCreditManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/flow/WindowCreditManagerTest.java new file mode 100644 index 0000000000..2011dfbda6 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/flow/WindowCreditManagerTest.java @@ -0,0 +1,83 @@ +/* + * + * 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.qpid.server.flow; + +import org.apache.qpid.test.utils.QpidTestCase; + +public class WindowCreditManagerTest extends QpidTestCase +{ + WindowCreditManager _creditManager; + + protected void setUp() throws Exception + { + super.setUp(); + _creditManager = new WindowCreditManager(); + } + + /** + * Tests that after the credit limit is cleared (e.g. from a message.stop command), credit is + * restored (e.g. from completed MessageTransfer) without increasing the available credit, and + * more credit is added, that the 'used' count is correct and the proper values for bytes + * and message credit are returned along with appropriate 'hasCredit' results (QPID-3592). + */ + public void testRestoreCreditDecrementsUsedCountAfterCreditClear() + { + assertEquals("unexpected credit value", 0, _creditManager.getMessageCredit()); + assertEquals("unexpected credit value", 0, _creditManager.getBytesCredit()); + + //give some message credit + _creditManager.addCredit(1, 0); + assertFalse("Manager should not 'haveCredit' due to having 0 bytes credit", _creditManager.hasCredit()); + assertEquals("unexpected credit value", 1, _creditManager.getMessageCredit()); + assertEquals("unexpected credit value", 0, _creditManager.getBytesCredit()); + + //give some bytes credit + _creditManager.addCredit(0, 1); + assertTrue("Manager should 'haveCredit'", _creditManager.hasCredit()); + assertEquals("unexpected credit value", 1, _creditManager.getMessageCredit()); + assertEquals("unexpected credit value", 1, _creditManager.getBytesCredit()); + + //use all the credit + _creditManager.useCreditForMessage(1); + assertEquals("unexpected credit value", 0, _creditManager.getBytesCredit()); + assertEquals("unexpected credit value", 0, _creditManager.getMessageCredit()); + assertFalse("Manager should not 'haveCredit'", _creditManager.hasCredit()); + + //clear credit out (eg from a message.stop command) + _creditManager.clearCredit(); + assertEquals("unexpected credit value", 0, _creditManager.getBytesCredit()); + assertEquals("unexpected credit value", 0, _creditManager.getMessageCredit()); + assertFalse("Manager should not 'haveCredit'", _creditManager.hasCredit()); + + //restore credit (e.g the original message transfer command got completed) + //this should not increase credit, because it is now limited to 0 + _creditManager.restoreCredit(1, 1); + assertEquals("unexpected credit value", 0, _creditManager.getBytesCredit()); + assertEquals("unexpected credit value", 0, _creditManager.getMessageCredit()); + assertFalse("Manager should not 'haveCredit'", _creditManager.hasCredit()); + + //give more credit to open the window again + _creditManager.addCredit(1, 1); + assertEquals("unexpected credit value", 1, _creditManager.getBytesCredit()); + assertEquals("unexpected credit value", 1, _creditManager.getMessageCredit()); + assertTrue("Manager should 'haveCredit'", _creditManager.hasCredit()); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java index 4df051edb5..e1dae5fcc1 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java @@ -20,23 +20,21 @@ */ package org.apache.qpid.server.protocol; -import junit.framework.TestCase; +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.SkeletonMessageStore; - -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularData; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; /** Test class to test MBean operations for AMQMinaProtocolSession. */ @@ -67,7 +65,7 @@ public class AMQProtocolSessionMBeanTest extends InternalBrokerBaseCase assertTrue(channelCount == 2); // general properties test - _mbean.setMaximumNumberOfChannels(1000L); + _protocolSession.setMaximumNumberOfChannels(1000L); assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L); // check APIs diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index 5a411c6807..3c76252cb2 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -43,6 +43,9 @@ import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.TestNetworkConnection; @@ -120,6 +123,11 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr { } + public ClientDeliveryMethod createDeliveryMethod(int channelId) + { + return new InternalWriteDeliverMethod(channelId); + } + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) { } @@ -213,4 +221,41 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr ((AMQChannel)session).getProtocolSession().closeSession(); } + + private class InternalWriteDeliverMethod implements ClientDeliveryMethod + { + private int _channelId; + + public InternalWriteDeliverMethod(int channelId) + { + _channelId = channelId; + } + + + public void deliverToClient(Subscription sub, QueueEntry entry, long deliveryTag) throws AMQException + { + _deliveryCount.incrementAndGet(); + + synchronized (_channelDelivers) + { + Map<AMQShortString, LinkedList<DeliveryPair>> consumers = _channelDelivers.get(_channelId); + + if (consumers == null) + { + consumers = new HashMap<AMQShortString, LinkedList<DeliveryPair>>(); + _channelDelivers.put(_channelId, consumers); + } + + LinkedList<DeliveryPair> consumerDelivers = consumers.get(((SubscriptionImpl)sub).getConsumerTag()); + + if (consumerDelivers == null) + { + consumerDelivers = new LinkedList<DeliveryPair>(); + consumers.put(((SubscriptionImpl)sub).getConsumerTag(), consumerDelivers); + } + + consumerDelivers.add(new DeliveryPair(deliveryTag, (AMQMessage)entry.getMessage())); + } + } + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java index 3961b3b355..2ce43052d9 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java @@ -21,12 +21,12 @@ package org.apache.qpid.server.queue; */ import java.util.ArrayList; - +import junit.framework.AssertionFailedError; import org.apache.qpid.AMQException; -import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.FieldTable; -import junit.framework.AssertionFailedError; +import org.apache.qpid.server.message.AMQMessage; public class AMQPriorityQueueTest extends SimpleAMQQueueTest { @@ -35,7 +35,7 @@ public class AMQPriorityQueueTest extends SimpleAMQQueueTest public void setUp() throws Exception { _arguments = new FieldTable(); - _arguments.put(AMQQueueFactory.X_QPID_PRIORITIES, 3); + _arguments.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), 3); super.setUp(); } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java index 27891289fb..2b7d1d7f26 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java @@ -20,39 +20,76 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.DefaultExchangeFactory; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.util.TestApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.test.utils.QpidTestCase; -public class AMQQueueFactoryTest extends InternalBrokerBaseCase +public class AMQQueueFactoryTest extends QpidTestCase { QueueRegistry _queueRegistry; VirtualHost _virtualHost; - int _defaultQueueCount; @Override public void setUp() throws Exception { super.setUp(); - ApplicationRegistry registry = (ApplicationRegistry) ApplicationRegistry.getInstance(); - _virtualHost = registry.getVirtualHostRegistry().getVirtualHost("test"); + CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); + + XMLConfiguration configXml = new XMLConfiguration(); + configXml.addProperty("virtualhosts.virtualhost(-1).name", getName()); + configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); + + ServerConfiguration configuration = new ServerConfiguration(configXml); + + ApplicationRegistry registry = new TestApplicationRegistry(configuration); + ApplicationRegistry.initialise(registry); + registry.getVirtualHostRegistry().setDefaultVirtualHostName(getName()); + + _virtualHost = registry.getVirtualHostRegistry().getVirtualHost(getName()); _queueRegistry = _virtualHost.getQueueRegistry(); - _defaultQueueCount = _queueRegistry.getQueues().size(); } @Override public void tearDown() throws Exception { - assertEquals("Queue was not registered in virtualhost", _defaultQueueCount + 1, _queueRegistry.getQueues().size()); - super.tearDown(); + try + { + super.tearDown(); + } + finally + { + ApplicationRegistry.remove(); + } + } + + private void verifyRegisteredQueueCount(int count) + { + assertEquals("Queue was not registered in virtualhost", count, _queueRegistry.getQueues().size()); } + private void verifyQueueRegistered(String queueName) + { + assertNotNull("Queue " + queueName + " was not created", _queueRegistry.getQueue(queueName)); + } + public void testPriorityQueueRegistration() throws Exception { FieldTable fieldTable = new FieldTable(); @@ -63,13 +100,314 @@ public class AMQQueueFactoryTest extends InternalBrokerBaseCase false, _virtualHost, fieldTable); assertEquals("Queue not a priorty queue", AMQPriorityQueue.class, queue.getClass()); + verifyQueueRegistered("testPriorityQueue"); + verifyRegisteredQueueCount(1); } public void testSimpleQueueRegistration() throws Exception { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("owner"), false, + AMQShortString queueName = new AMQShortString("testSimpleQueueRegistration"); + AMQShortString dlQueueName = new AMQShortString(queueName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, _virtualHost, null); assertEquals("Queue not a simple queue", SimpleAMQQueue.class, queue.getClass()); + verifyQueueRegistered("testSimpleQueueRegistration"); + + //verify that no alternate exchange or DLQ were produced + QueueRegistry qReg = _virtualHost.getQueueRegistry(); + + assertNull("Queue should not have an alternate exchange as DLQ wasnt enabled", queue.getAlternateExchange()); + assertNull("The DLQ should not exist", qReg.getQueue(dlQueueName)); + + verifyRegisteredQueueCount(1); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_DLQ_ENABLED} argument true does + * cause the alternate exchange to be set and DLQ to be produced. + * @throws AMQException + */ + public void testDeadLetterQueueEnabled() throws AMQException + { + FieldTable fieldTable = new FieldTable(); + fieldTable.setBoolean(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); + + AMQShortString queueName = new AMQShortString("testDeadLetterQueueEnabled"); + AMQShortString dlExchangeName = new AMQShortString(queueName + DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + AMQShortString dlQueueName = new AMQShortString(queueName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + + QueueRegistry qReg = _virtualHost.getQueueRegistry(); + ExchangeRegistry exReg = _virtualHost.getExchangeRegistry(); + + assertNull("The DLQ should not yet exist", qReg.getQueue(dlQueueName)); + assertNull("The alternate exchange should not yet exist", exReg.getExchange(dlExchangeName)); + + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, + _virtualHost, fieldTable); + + Exchange altExchange = queue.getAlternateExchange(); + assertNotNull("Queue should have an alternate exchange as DLQ is enabled", altExchange); + assertEquals("Alternate exchange name was not as expected", dlExchangeName, altExchange.getName()); + assertEquals("Alternate exchange type was not as expected", ExchangeDefaults.FANOUT_EXCHANGE_CLASS, altExchange.getType().getName()); + + assertNotNull("The alternate exchange was not registered as expected", exReg.getExchange(dlExchangeName)); + assertEquals("The registered exchange was not the expected exchange instance", altExchange, exReg.getExchange(dlExchangeName)); + + AMQQueue dlQueue = qReg.getQueue(dlQueueName); + assertNotNull("The DLQ was not registered as expected", dlQueue); + assertTrue("DLQ should have been bound to the alternate exchange", altExchange.isBound(dlQueue)); + assertNull("DLQ should have no alternate exchange", dlQueue.getAlternateExchange()); + assertEquals("DLQ should have a zero maximum delivery count", 0, dlQueue.getMaximumDeliveryCount()); + + //2 queues should have been registered + verifyRegisteredQueueCount(2); + } + + /** + * Tests that the deadLetterQueues/maximumDeliveryCount settings from the configuration + * are not applied to the DLQ itself. + * @throws AMQException + */ + public void testDeadLetterQueueDoesNotInheritDLQorMDCSettings() throws AMQException + { + ApplicationRegistry.getInstance().getConfiguration().getConfig().addProperty("deadLetterQueues","true"); + ApplicationRegistry.getInstance().getConfiguration().getConfig().addProperty("maximumDeliveryCount","5"); + + AMQShortString queueName = new AMQShortString("testDeadLetterQueueEnabled"); + AMQShortString dlExchangeName = new AMQShortString(queueName + DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + AMQShortString dlQueueName = new AMQShortString(queueName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + + QueueRegistry qReg = _virtualHost.getQueueRegistry(); + ExchangeRegistry exReg = _virtualHost.getExchangeRegistry(); + + assertNull("The DLQ should not yet exist", qReg.getQueue(dlQueueName)); + assertNull("The alternate exchange should not yet exist", exReg.getExchange(dlExchangeName)); + + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, + _virtualHost, null); + + assertEquals("Unexpected maximum delivery count", 5, queue.getMaximumDeliveryCount()); + Exchange altExchange = queue.getAlternateExchange(); + assertNotNull("Queue should have an alternate exchange as DLQ is enabled", altExchange); + assertEquals("Alternate exchange name was not as expected", dlExchangeName, altExchange.getName()); + assertEquals("Alternate exchange type was not as expected", ExchangeDefaults.FANOUT_EXCHANGE_CLASS, altExchange.getType().getName()); + + assertNotNull("The alternate exchange was not registered as expected", exReg.getExchange(dlExchangeName)); + assertEquals("The registered exchange was not the expected exchange instance", altExchange, exReg.getExchange(dlExchangeName)); + + AMQQueue dlQueue = qReg.getQueue(dlQueueName); + assertNotNull("The DLQ was not registered as expected", dlQueue); + assertTrue("DLQ should have been bound to the alternate exchange", altExchange.isBound(dlQueue)); + assertNull("DLQ should have no alternate exchange", dlQueue.getAlternateExchange()); + assertEquals("DLQ should have a zero maximum delivery count", 0, dlQueue.getMaximumDeliveryCount()); + + //2 queues should have been registered + verifyRegisteredQueueCount(2); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_DLQ_ENABLED} argument false does not + * result in the alternate exchange being set and DLQ being created. + * @throws AMQException + */ + public void testDeadLetterQueueDisabled() throws AMQException + { + FieldTable fieldTable = new FieldTable(); + fieldTable.setBoolean(AMQQueueFactory.X_QPID_DLQ_ENABLED, false); + + AMQShortString queueName = new AMQShortString("testDeadLetterQueueDisabled"); + AMQShortString dlExchangeName = new AMQShortString(queueName + DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + AMQShortString dlQueueName = new AMQShortString(queueName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + + QueueRegistry qReg = _virtualHost.getQueueRegistry(); + ExchangeRegistry exReg = _virtualHost.getExchangeRegistry(); + + assertNull("The DLQ should not yet exist", qReg.getQueue(dlQueueName)); + assertNull("The alternate exchange should not exist", exReg.getExchange(dlExchangeName)); + + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, + _virtualHost, fieldTable); + + assertNull("Queue should not have an alternate exchange as DLQ is disabled", queue.getAlternateExchange()); + assertNull("The alternate exchange should still not exist", exReg.getExchange(dlExchangeName)); + + assertNull("The DLQ should still not exist", qReg.getQueue(dlQueueName)); + + //only 1 queue should have been registered + verifyRegisteredQueueCount(1); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_DLQ_ENABLED} argument true but + * creating an auto-delete queue, does not result in the alternate exchange + * being set and DLQ being created. + * @throws AMQException + */ + public void testDeadLetterQueueNotCreatedForAutodeleteQueues() throws AMQException + { + FieldTable fieldTable = new FieldTable(); + fieldTable.setBoolean(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); + + AMQShortString queueName = new AMQShortString("testDeadLetterQueueNotCreatedForAutodeleteQueues"); + AMQShortString dlExchangeName = new AMQShortString(queueName + DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + AMQShortString dlQueueName = new AMQShortString(queueName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + + QueueRegistry qReg = _virtualHost.getQueueRegistry(); + ExchangeRegistry exReg = _virtualHost.getExchangeRegistry(); + + assertNull("The DLQ should not yet exist", qReg.getQueue(dlQueueName)); + assertNull("The alternate exchange should not exist", exReg.getExchange(dlExchangeName)); + + //create an autodelete queue + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), true, false, + _virtualHost, fieldTable); + assertTrue("Queue should be autodelete", queue.isAutoDelete()); + + //ensure that the autodelete property overrides the request to enable DLQ + assertNull("Queue should not have an alternate exchange as queue is autodelete", queue.getAlternateExchange()); + assertNull("The alternate exchange should not exist as queue is autodelete", exReg.getExchange(dlExchangeName)); + assertNull("The DLQ should not exist as queue is autodelete", qReg.getQueue(dlQueueName)); + + //only 1 queue should have been registered + verifyRegisteredQueueCount(1); + } + + /** + * Tests that setting the {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument has + * the desired effect. + */ + public void testMaximumDeliveryCount() throws Exception + { + final FieldTable fieldTable = new FieldTable(); + fieldTable.setInteger(AMQQueueFactory.X_QPID_MAXIMUM_DELIVERY_COUNT, 5); + + final AMQShortString queueName = new AMQShortString("testMaximumDeliveryCount"); + + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, + _virtualHost, fieldTable); + + assertNotNull("The queue was not registered as expected ", queue); + assertEquals("Maximum delivery count not as expected", 5, queue.getMaximumDeliveryCount()); + + verifyRegisteredQueueCount(1); + } + + /** + * Tests that omitting the {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument means + * that queue is created with a default maximumDeliveryCount of zero (unless set in config). + */ + public void testMaximumDeliveryCountDefault() throws Exception + { + + final AMQShortString queueName = new AMQShortString("testMaximumDeliveryCount"); + + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, new AMQShortString("owner"), false, false, + _virtualHost, null); + + assertNotNull("The queue was not registered as expected ", queue); + assertEquals("Maximum delivery count not as expected", 0, queue.getMaximumDeliveryCount()); + + verifyRegisteredQueueCount(1); + } + + /** + * Tests queue creation with queue name set to null + */ + public void testQueueNameNullValidation() + { + try + { + AMQQueueFactory.createAMQQueueImpl(null, false, new AMQShortString("owner"), true, false, _virtualHost, null); + fail("queue with null name can not be created!"); + } + catch (Exception e) + { + assertTrue(e instanceof IllegalArgumentException); + assertEquals("Queue name must not be null", e.getMessage()); + } + } + + /** + * Tests queue creation with queue name length less 255 characters but + * corresponding DLQ name length greater than 255. + */ + public void testQueueNameWithLengthLessThan255ButDLQNameWithLengthGreaterThan255() + { + String queueName = "test-" + generateStringWithLength('a', 245); + try + { + // change DLQ name to make its length bigger than exchange name + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterExchangeSuffix", "_DLE"); + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterQueueSuffix", "_DLQUEUE"); + + FieldTable fieldTable = new FieldTable(); + fieldTable.setBoolean(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); + AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), false, new AMQShortString("owner"), + false, false, _virtualHost, fieldTable); + fail("queue with DLQ name having more than 255 characters can not be created!"); + } + catch (Exception e) + { + assertTrue("Unexpected exception is thrown!", e instanceof IllegalArgumentException); + assertTrue("Unexpected exception message!", e.getMessage().contains("DLQ queue name") + && e.getMessage().contains("length exceeds limit of 255")); + } + finally + { + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterExchangeSuffix", DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + } + + /** + * Tests queue creation with queue name length less 255 characters but + * corresponding DL exchange name length greater than 255. + */ + public void testQueueNameWithLengthLessThan255ButDLExchangeNameWithLengthGreaterThan255() + { + String queueName = "test-" + generateStringWithLength('a', 245); + try + { + // change DLQ name to make its length bigger than exchange name + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterExchangeSuffix", "_DLEXCHANGE"); + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterQueueSuffix", "_DLQ"); + + FieldTable fieldTable = new FieldTable(); + fieldTable.setBoolean(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); + AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), false, new AMQShortString("owner"), + false, false, _virtualHost, fieldTable); + fail("queue with DLE name having more than 255 characters can not be created!"); + } + catch (Exception e) + { + assertTrue("Unexpected exception is thrown!", e instanceof IllegalArgumentException); + assertTrue("Unexpected exception message!", e.getMessage().contains("DL exchange name") + && e.getMessage().contains("length exceeds limit of 255")); + } + finally + { + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterExchangeSuffix", DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + ApplicationRegistry.getInstance().getConfiguration().getConfig() + .addProperty("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + } + + private String generateStringWithLength(char ch, int length) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) + { + sb.append(ch); + } + return sb.toString(); } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java index 070d105805..f70250132a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -20,13 +20,14 @@ */ package org.apache.qpid.server.queue; +import org.apache.commons.lang.time.FastDateFormat; import org.apache.qpid.AMQException; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.message.AMQMessage; @@ -39,11 +40,21 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; /** - * Test class to test AMQQueueMBean attribtues and operations + * Test class to test AMQQueueMBean attributes and operations */ public class AMQQueueMBeanTest extends InternalBrokerBaseCase { @@ -139,6 +150,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase verifyBrokerState(); } + // todo: collect to a general testing class -duplicated from Systest/MessageReturntest private void verifyBrokerState() { @@ -219,7 +231,43 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase assertFalse("Exclusive property should be false.", getQueue().isExclusive()); } - public void testExceptions() throws Exception + /** + * Tests view messages with two test messages. The first message is non-persistent, the second persistent + * and has timestamp/expiration. + * + */ + public void testViewMessages() throws Exception + { + sendMessages(1, false); + final Date msg2Timestamp = new Date(); + final Date msg2Expiration = new Date(msg2Timestamp.getTime() + 1000); + sendMessages(1, true, msg2Timestamp.getTime(), msg2Expiration.getTime()); + + final TabularData tab = _queueMBean.viewMessages(1l, 2l); + assertEquals("Unexpected number of rows in table", 2, tab.size()); + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) tab.values().iterator(); + + // Check row1 + final CompositeDataSupport row1 = rowItr.next(); + assertEquals("Message should have AMQ message id", 1l, row1.get(ManagedQueue.MSG_AMQ_ID)); + assertNotNull("Expected message header array", row1.get(ManagedQueue.MSG_HEADER)); + final Map<String, String> row1Headers = headerArrayToMap((String[])row1.get(ManagedQueue.MSG_HEADER)); + assertEquals("Unexpected JMSPriority within header", "Non_Persistent", row1Headers.get("JMSDeliveryMode")); + assertEquals("Unexpected JMSTimestamp within header", "null", row1Headers.get("JMSTimestamp")); + assertEquals("Unexpected JMSExpiration within header", "null", row1Headers.get("JMSExpiration")); + + final CompositeDataSupport row2 = rowItr.next(); + assertEquals("Message should have AMQ message id", 2l, row2.get(ManagedQueue.MSG_AMQ_ID)); + assertNotNull("Expected message header array", row2.get(ManagedQueue.MSG_HEADER)); + final Map<String, String> row2Headers = headerArrayToMap((String[])row2.get(ManagedQueue.MSG_HEADER)); + assertEquals("Unexpected JMSPriority within header", "Persistent", row2Headers.get("JMSDeliveryMode")); + assertEquals("Unexpected JMSTimestamp within header", FastDateFormat.getInstance(AMQQueueMBean.JMSTIMESTAMP_DATETIME_FORMAT).format(msg2Timestamp), + row2Headers.get("JMSTimestamp")); + assertEquals("Unexpected JMSExpiration within header", FastDateFormat.getInstance(AMQQueueMBean.JMSTIMESTAMP_DATETIME_FORMAT).format(msg2Expiration), + row2Headers.get("JMSExpiration")); + } + + public void testViewMessageWithIllegalStartEndRanges() throws Exception { try { @@ -228,7 +276,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase } catch (JMException ex) { - + // PASS } try @@ -238,7 +286,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase } catch (JMException ex) { - + // PASS } try @@ -248,7 +296,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase } catch (JMException ex) { - + // PASS } try @@ -260,45 +308,24 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase } catch (JMException ex) { - + // PASS } + } - IncomingMessage msg = message(false, false); - getQueue().clearQueue(); - ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); - qs.add(getQueue()); - msg.enqueue(qs); - MessageMetaData mmd = msg.headersReceived(); - msg.setStoredMessage(getMessageStore().addMessage(mmd)); - long id = msg.getMessageNumber(); - - msg.addContentBodyFrame(new ContentChunk() - { - byte[] _data = new byte[((int)MESSAGE_SIZE)]; - - public int getSize() - { - return (int) MESSAGE_SIZE; - } - - public byte[] getData() - { - return _data; - } + public void testViewMessageContent() throws Exception + { + final List<AMQMessage> sentMessages = sendMessages(1, true); + final Long id = sentMessages.get(0).getMessageId(); - public void reduceToFit() - { + final CompositeData messageData = _queueMBean.viewMessageContent(id); + assertNotNull(messageData); + } - } - }); + public void testViewMessageContentWithUnknownMessageId() throws Exception + { + final List<AMQMessage> sentMessages = sendMessages(1, true); + final Long id = sentMessages.get(0).getMessageId(); - AMQMessage m = new AMQMessage(msg.getStoredMessage()); - for(BaseQueue q : msg.getDestinationQueues()) - { - q.enqueue(m); - } -// _queue.process(_storeContext, new QueueEntry(_queue, msg), false); - _queueMBean.viewMessageContent(id); try { _queueMBean.viewMessageContent(id + 1); @@ -306,7 +333,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase } catch (JMException ex) { - + // PASS } } @@ -364,47 +391,35 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase assertFalse(channel.getBlocking()); } - private IncomingMessage message(final boolean immediate, boolean persistent) throws AMQException + public void testMaximumDeliveryCount() throws IOException { - MessagePublishInfo publish = new MessagePublishInfo() - { + assertEquals("Unexpected default maximum delivery count", Integer.valueOf(0), _queueMBean.getMaximumDeliveryCount()); + } - public AMQShortString getExchange() - { - return null; - } + public void testViewAllMessages() throws Exception + { + final int messageCount = 5; + sendPersistentMessages(messageCount); - public void setExchange(AMQShortString exchange) - { - //To change body of implemented methods use File | Settings | File Templates. - } - public boolean isImmediate() - { - return immediate; - } + final TabularData messageTable = _queueMBean.viewMessages(1L, 5L); + assertNotNull("Message table should not be null", messageTable); + assertEquals("Unexpected number of rows", messageCount, messageTable.size()); - public boolean isMandatory() - { - return false; - } - public AMQShortString getRoutingKey() - { - return null; - } - }; - - ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); - contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes - contentHeaderBody.setProperties(new BasicContentHeaderProperties()); - ((BasicContentHeaderProperties) contentHeaderBody.getProperties()).setDeliveryMode((byte) (persistent ? 2 : 1)); - IncomingMessage msg = new IncomingMessage(publish); - msg.setContentHeaderBody(contentHeaderBody); - return msg; + final Iterator rowIterator = messageTable.values().iterator(); + // Get its message ID + final CompositeDataSupport row1 = (CompositeDataSupport) rowIterator.next(); + final Long msgId = (Long) row1.get("AMQ MessageId"); + final Long queuePosition = (Long) row1.get("Queue Position"); + final Integer deliveryCount = (Integer) row1.get("Delivery Count"); + assertNotNull("Row should have value for queue position", queuePosition); + assertNotNull("Row should have value for msgid", msgId); + assertNotNull("Row should have value for deliveryCount", deliveryCount); } + @Override public void setUp() throws Exception { @@ -418,11 +433,25 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase ApplicationRegistry.remove(); } - private void sendMessages(int messageCount, boolean persistent) throws AMQException + private void sendPersistentMessages(int messageCount) throws AMQException + { + sendMessages(messageCount, true); + assertEquals("Expected " + messageCount + " messages in the queue", messageCount, _queueMBean + .getMessageCount().intValue()); + } + + private List<AMQMessage> sendMessages(int messageCount, boolean persistent) throws AMQException + { + return sendMessages(messageCount, persistent, 0l, 0l); + } + + private List<AMQMessage> sendMessages(int messageCount, boolean persistent, long timestamp, long expiration) throws AMQException { + final List<AMQMessage> sentMessages = new ArrayList<AMQMessage>(); + for (int i = 0; i < messageCount; i++) { - IncomingMessage currentMessage = message(false, persistent); + IncomingMessage currentMessage = createIncomingMessage(false, persistent, timestamp, expiration); ArrayList<AMQQueue> qs = new ArrayList<AMQQueue>(); qs.add(getQueue()); currentMessage.enqueue(qs); @@ -431,7 +460,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase MessageMetaData mmd = currentMessage.headersReceived(); currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); - // Add the body so we have somthing to test later + // Add the body so we have something to test later currentMessage.addContentBodyFrame( getSession().getMethodRegistry() .getProtocolVersionMethodConverter() @@ -444,7 +473,78 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase q.enqueue(m); } + sentMessages.add(m); + } + return sentMessages; + } + + private IncomingMessage createIncomingMessage(final boolean immediate, boolean persistent, long timestamp, long expiration) throws AMQException + { + MessagePublishInfo publish = new MessagePublishInfo() + { + + public AMQShortString getExchange() + { + return null; + } + + public void setExchange(AMQShortString exchange) + { + } + + public boolean isImmediate() + { + return immediate; + } + + public boolean isMandatory() + { + return false; + } + + public AMQShortString getRoutingKey() + { + return null; + } + }; + + ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); + contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes + final BasicContentHeaderProperties props = new BasicContentHeaderProperties(); + contentHeaderBody.setProperties(props); + props.setDeliveryMode((byte) (persistent ? 2 : 1)); + if (timestamp > 0) + { + props.setTimestamp(timestamp); + } + if (expiration > 0) + { + props.setExpiration(expiration); } + IncomingMessage msg = new IncomingMessage(publish); + msg.setContentHeaderBody(contentHeaderBody); + return msg; } + + /** + * + * Utility method to convert array of Strings in the form x = y into a + * map with key/value x => y. + * + */ + private Map<String,String> headerArrayToMap(final String[] headerArray) + { + final Map<String, String> headerMap = new HashMap<String, String>(); + final List<String> headerList = Arrays.asList(headerArray); + for (Iterator<String> iterator = headerList.iterator(); iterator.hasNext();) + { + final String nameValuePair = iterator.next(); + final String[] nameValue = nameValuePair.split(" *= *", 2); + headerMap.put(nameValue[0], nameValue[1]); + } + return headerMap; + } + + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java index 0f5374b3e5..5d559c9d0d 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java @@ -30,6 +30,7 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; +import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.util.InternalBrokerBaseCase; @@ -143,16 +144,19 @@ public class AckTest extends InternalBrokerBaseCase qs.add(_queue); msg.enqueue(qs); MessageMetaData mmd = msg.headersReceived(); - msg.setStoredMessage(_messageStore.addMessage(mmd)); + final StoredMessage storedMessage = _messageStore.addMessage(mmd); + msg.setStoredMessage(storedMessage); + final AMQMessage message = new AMQMessage(storedMessage); if(msg.allContentReceived()) { ServerTransaction txn = new AutoCommitTransaction(_messageStore); - txn.enqueue(_queue, msg, new ServerTransaction.Action() { + txn.enqueue(_queue, message, new ServerTransaction.Action() { public void postCommit() { try { - _queue.enqueue(new AMQMessage(msg.getStoredMessage())); + + _queue.enqueue(message); } catch (AMQException e) { @@ -170,6 +174,15 @@ public class AckTest extends InternalBrokerBaseCase // we manually send the message to the subscription //_subscription.send(new QueueEntry(_queue,msg), _queue); } + try + { + Thread.sleep(2000L); + } + catch (InterruptedException e) + { + e.printStackTrace(); //TODO. + } + } /** @@ -181,9 +194,8 @@ public class AckTest extends InternalBrokerBaseCase _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true, null, false, new LimitlessCreditManager()); final int msgCount = 10; publishMessages(msgCount, true); - UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap(); - assertEquals("",msgCount,map.size()); + assertEquals("Unextpected size for unacknowledge message map",msgCount,map.size()); Set<Long> deliveryTagSet = map.getDeliveryTags(); int i = 1; @@ -206,7 +218,6 @@ public class AckTest extends InternalBrokerBaseCase _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, false, null, false, new LimitlessCreditManager()); final int msgCount = 10; publishMessages(msgCount); - UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap(); assertTrue(map.size() == 0); assertTrue(_messageStore.getMessageCount() == 0); @@ -243,7 +254,7 @@ public class AckTest extends InternalBrokerBaseCase _channel.acknowledgeMessage(5, false); UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap(); - assertTrue(map.size() == msgCount - 1); + assertEquals("Map not expected size",msgCount - 1,map.size()); Set<Long> deliveryTagSet = map.getDeliveryTags(); int i = 1; @@ -270,6 +281,8 @@ public class AckTest extends InternalBrokerBaseCase final int msgCount = 10; publishMessages(msgCount); + + _channel.acknowledgeMessage(5, true); UnacknowledgedMessageMap map = _channel.getUnacknowledgedMessageMap(); assertTrue(map.size() == 5); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java index 7000df157e..12369bd7d4 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java @@ -20,24 +20,23 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; public class MockAMQMessage extends AMQMessage { public MockAMQMessage(long messageId) - throws AMQException { super(new MockStoredMessage(messageId)); } - - + public MockAMQMessage(long messageId, String headerName, Object headerValue) + { + super(new MockStoredMessage(messageId, headerName, headerValue)); + } @Override public long getSize() { return 0l; } - } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index 4c31092983..0daf79122c 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -611,4 +611,21 @@ public class MockAMQQueue implements AMQQueue { } + + @Override + public int getMaximumDeliveryCount() + { + return 0; + } + + @Override + public void setMaximumDeliveryCount(int maximumDeliveryCount) + { + } + + @Override + public void setAlternateExchange(String exchangeName) + { + } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java index ab8850c18c..7ad002c248 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java @@ -234,4 +234,31 @@ public class MockQueueEntry implements QueueEntry return false; } + public QueueEntry getNextNode() + { + return null; + } + + public QueueEntry getNextValidEntry() + { + return null; + } + + @Override + public int getDeliveryCount() + { + return 0; + } + + @Override + public void incrementDeliveryCount() + { + } + + @Override + public void decrementDeliveryCount() + { + } + + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java index 0f812c9c7a..e2418a85be 100755 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.queue; +import org.apache.qpid.framing.FieldTable; + import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TransactionLog; import org.apache.qpid.server.store.StoredMessage; @@ -36,16 +38,32 @@ public class MockStoredMessage implements StoredMessage<MessageMetaData> private MessageMetaData _metaData; private ByteBuffer _content; - public MockStoredMessage(long messageId) { - this(messageId, new MockMessagePublishInfo(), new ContentHeaderBody(new BasicContentHeaderProperties(), 60)); + this(messageId, (String)null, null); + } + + public MockStoredMessage(long messageId, String headerName, Object headerValue) + { + this(messageId, new MockMessagePublishInfo(), new ContentHeaderBody(new BasicContentHeaderProperties(), 60), headerName, headerValue); } public MockStoredMessage(long messageId, MessagePublishInfo info, ContentHeaderBody chb) { + this(messageId, info, chb, null, null); + } + + public MockStoredMessage(long messageId, MessagePublishInfo info, ContentHeaderBody chb, String headerName, Object headerValue) + { _messageId = messageId; + if (headerName != null) + { + FieldTable headers = new FieldTable(); + headers.setString(headerName, headerValue == null? null :String.valueOf(headerValue)); + ((BasicContentHeaderProperties)chb.getProperties()).setHeaders(headers); + } _metaData = new MessageMetaData(info, chb, 0); + _content = ByteBuffer.allocate(_metaData.getContentSize()); } public MessageMetaData getMetaData() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java index d8afd8d829..d336132316 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java @@ -19,9 +19,7 @@ package org.apache.qpid.server.queue; import java.lang.reflect.Field; - import junit.framework.TestCase; - import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.queue.QueueEntry.EntryState; @@ -30,18 +28,27 @@ import org.apache.qpid.server.subscription.Subscription; /** * Tests for {@link QueueEntryImpl} - * */ -public class QueueEntryImplTest extends TestCase +public abstract class QueueEntryImplTestBase extends TestCase { // tested entry - private QueueEntryImpl _queueEntry; + protected QueueEntryImpl _queueEntry; + protected QueueEntryImpl _queueEntry2; + protected QueueEntryImpl _queueEntry3; + + public abstract QueueEntryImpl getQueueEntryImpl(int msgid) throws AMQException; + + public abstract void testCompareTo(); + + public abstract void testTraverseWithNoDeletedEntries(); + + public abstract void testTraverseWithDeletedEntries(); public void setUp() throws Exception { - AMQMessage message = new MockAMQMessage(1); - SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); - _queueEntry = new QueueEntryImpl(queueEntryList, message, 1); + _queueEntry = getQueueEntryImpl(1); + _queueEntry2 = getQueueEntryImpl(2); + _queueEntry3 = getQueueEntryImpl(3); } public void testAquire() @@ -105,61 +112,6 @@ public class QueueEntryImplTest extends TestCase } /** - * Tests if entries in DEQUQUED or DELETED state are not returned by getNext method. - */ - public void testGetNext() - { - int numberOfEntries = 5; - QueueEntryImpl[] entries = new QueueEntryImpl[numberOfEntries]; - SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); - - // create test entries - for(int i = 0; i < numberOfEntries ; i++) - { - AMQMessage message = null;; - try - { - message = new MockAMQMessage(i); - } - catch (AMQException e) - { - fail("Failure to create a mock message:" + e.getMessage()); - } - QueueEntryImpl entry = (QueueEntryImpl)queueEntryList.add(message); - entries[i] = entry; - } - - // test getNext for not acquired entries - for(int i = 0; i < numberOfEntries ; i++) - { - QueueEntryImpl queueEntry = entries[i]; - QueueEntryImpl next = queueEntry.getNext(); - if (i < numberOfEntries - 1) - { - assertEquals("Unexpected entry from QueueEntryImpl#getNext()", entries[i + 1], next); - } - else - { - assertNull("The next entry after the last should be null", next); - } - } - - // delete second - entries[1].acquire(); - entries[1].delete(); - - // dequeue third - entries[2].acquire(); - entries[2].dequeue(); - - QueueEntryImpl next = entries[0].getNext(); - assertEquals("expected forth entry",entries[3], next); - next = next.getNext(); - assertEquals("expected fifth entry", entries[4], next); - next = next.getNext(); - assertNull("The next entry after the last should be null", next); - } - /** * A helper method to put tested object into deleted state and assert the state */ private void delete() @@ -244,4 +196,52 @@ public class QueueEntryImplTest extends TestCase assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(sub2Id)); } + + /** + * Tests if entries in DEQUQUED or DELETED state are not returned by getNext method. + */ + public void testGetNext() + { + int numberOfEntries = 5; + QueueEntryImpl[] entries = new QueueEntryImpl[numberOfEntries]; + SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); + + // create test entries + for(int i = 0; i < numberOfEntries ; i++) + { + AMQMessage message = new MockAMQMessage(i); + QueueEntryImpl entry = (QueueEntryImpl)queueEntryList.add(message); + entries[i] = entry; + } + + // test getNext for not acquired entries + for(int i = 0; i < numberOfEntries ; i++) + { + QueueEntryImpl queueEntry = entries[i]; + QueueEntry next = queueEntry.getNextValidEntry(); + if (i < numberOfEntries - 1) + { + assertEquals("Unexpected entry from QueueEntryImpl#getNext()", entries[i + 1], next); + } + else + { + assertNull("The next entry after the last should be null", next); + } + } + + // delete second + entries[1].acquire(); + entries[1].delete(); + + // dequeue third + entries[2].acquire(); + entries[2].dequeue(); + + QueueEntry next = entries[0].getNextValidEntry(); + assertEquals("expected forth entry",entries[3], next); + next = next.getNextValidEntry(); + assertEquals("expected fifth entry", entries[4], next); + next = next.getNextValidEntry(); + assertNull("The next entry after the last should be null", next); + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java new file mode 100644 index 0000000000..7a3f6f701c --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java @@ -0,0 +1,190 @@ +/* + * 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.qpid.server.queue; + +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; + +/** + * Abstract test class for QueueEntryList implementations. + */ +public abstract class QueueEntryListTestBase extends TestCase +{ + protected static final AMQQueue _testQueue = new MockAMQQueue("test"); + public abstract QueueEntryList<QueueEntry> getTestList(); + public abstract long getExpectedFirstMsgId(); + public abstract int getExpectedListLength(); + public abstract ServerMessage getTestMessageToAdd() throws AMQException; + + public void testGetQueue() + { + assertEquals("Unexpected head entry returned by getHead()", getTestList().getQueue(), _testQueue); + } + + /** + * Test to add a message with properties specific to the queue type. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getTestMessageToAdd() + * @throws AMQException + */ + public void testAddSpecificMessage() throws AMQException + { + final QueueEntryList<QueueEntry> list = getTestList(); + list.add(getTestMessageToAdd()); + + final QueueEntryIterator<?> iter = list.iterator(); + int count = 0; + while(iter.advance()) + { + iter.getNode(); + count++; + } + assertEquals("List did not grow by one entry after an add", getExpectedListLength() + 1, count); + } + + /** + * Test to add a generic mock message. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + * @see MockAMQMessage + * @throws AMQException + */ + public void testAddGenericMessage() throws AMQException + { + final QueueEntryList<QueueEntry> list = getTestList(); + list.add(new MockAMQMessage(666)); + + final QueueEntryIterator<?> iter = list.iterator(); + int count = 0; + while(iter.advance()) + { + iter.getNode(); + count++; + } + assertEquals("List did not grow by one entry after a generic message added", getExpectedListLength() + 1, count); + + } + + /** + * Test for getting the next element in a queue list. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + */ + public void testListNext() + { + final QueueEntryList<QueueEntry> entryList = getTestList(); + QueueEntry entry = entryList.getHead(); + int count = 0; + while(entryList.next(entry) != null) + { + entry = entryList.next(entry); + count++; + } + assertEquals("Get next didnt get all the list entries", getExpectedListLength(), count); + } + + /** + * Basic test for the associated QueueEntryIterator implementation. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + */ + public void testIterator() + { + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count = 0; + while(iter.advance()) + { + iter.getNode(); + count++; + } + assertEquals("Iterator invalid", getExpectedListLength(), count); + } + + /** + * Test for associated QueueEntryIterator implementation that checks it handles "removed" messages. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + */ + public void testDequedMessagedNotPresentInIterator() throws Exception + { + final int numberOfMessages = getExpectedListLength(); + final QueueEntryList<QueueEntry> entryList = getTestList(); + + // dequeue all even messages + final QueueEntryIterator<?> it1 = entryList.iterator(); + int counter = 0; + while (it1.advance()) + { + final QueueEntry queueEntry = it1.getNode(); + if(counter++ % 2 == 0) + { + queueEntry.acquire(); + queueEntry.dequeue(); + } + } + + // iterate and check that dequeued messages are not returned by iterator + final QueueEntryIterator<?> it2 = entryList.iterator(); + int counter2 = 0; + while(it2.advance()) + { + it2.getNode(); + counter2++; + } + final int expectedNumber = numberOfMessages / 2; + assertEquals("Expected " + expectedNumber + " number of entries in iterator but got " + counter2, + expectedNumber, counter2); + } + + /** + * Test to verify the head of the queue list is returned as expected. + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedFirstMsgId() + */ + public void testGetHead() + { + final QueueEntry head = getTestList().getHead(); + assertNull("Head entry should not contain an actual message", head.getMessage()); + assertEquals("Unexpected message id for first list entry", getExpectedFirstMsgId(), getTestList().next(head) + .getMessage().getMessageNumber().longValue()); + } + + /** + * Test to verify the entry deletion handled correctly. + * @see QueueEntryListTestBase#getTestList() + */ + public void testEntryDeleted() + { + final QueueEntry head = getTestList().getHead(); + + final QueueEntry first = getTestList().next(head); + first.delete(); + + final QueueEntry second = getTestList().next(head); + assertNotSame("After deletion the next entry should be different", first.getMessage().getMessageNumber(), second + .getMessage().getMessageNumber()); + + final QueueEntry third = getTestList().next(first); + assertEquals("After deletion the deleted nodes next node should be the same as the next from head", second + .getMessage().getMessageNumber(), third.getMessage().getMessageNumber()); + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java deleted file mode 100644 index b67723dd25..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.qpid.test.utils.QpidTestCase; - -/** - * - * Tests QueueEntry - * - */ -public class QueueEntryTest extends QpidTestCase -{ - private QueueEntryImpl _queueEntry1 = null; - private QueueEntryImpl _queueEntry2 = null; - private QueueEntryImpl _queueEntry3 = null; - - @Override - protected void setUp() throws Exception - { - super.setUp(); - - int i = 0; - - SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(null); - _queueEntry1 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++)); - _queueEntry2 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++)); - _queueEntry3 = (QueueEntryImpl) queueEntryList.add(new MockAMQMessage(i++)); - } - - public void testCompareTo() - { - assertTrue(_queueEntry1.compareTo(_queueEntry2) < 0); - assertTrue(_queueEntry2.compareTo(_queueEntry1) > 0); - assertTrue(_queueEntry1.compareTo(_queueEntry1) == 0); - } - - /** - * Tests that the getNext() can be used to traverse the list. - */ - public void testTraverseWithNoDeletedEntries() - { - QueueEntryImpl current = _queueEntry1; - - current = current.getNext(); - assertSame("Unexpected current entry",_queueEntry2, current); - - current = current.getNext(); - assertSame("Unexpected current entry",_queueEntry3, current); - - current = current.getNext(); - assertNull(current); - - } - - /** - * Tests that the getNext() can be used to traverse the list but deleted - * entries are skipped and de-linked from the chain of entries. - */ - public void testTraverseWithDeletedEntries() - { - // Delete 2nd queue entry - _queueEntry2.delete(); - assertTrue(_queueEntry2.isDeleted()); - - - QueueEntryImpl current = _queueEntry1; - - current = current.getNext(); - assertSame("Unexpected current entry",_queueEntry3, current); - - current = current.getNext(); - assertNull(current); - - // Assert the side effects of getNext() - assertSame("Next node of entry 1 should now be entry 3", - _queueEntry3, _queueEntry1.nextNode()); - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SelfValidatingSortedQueueEntryList.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SelfValidatingSortedQueueEntryList.java new file mode 100644 index 0000000000..7ff693e4c4 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SelfValidatingSortedQueueEntryList.java @@ -0,0 +1,160 @@ +/* + * 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.qpid.server.queue; + +import junit.framework.Assert; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.queue.SortedQueueEntryImpl.Colour; + +/** + * Test extension of SortedQueueEntryList that provides data structure validation tests. + * @see SortedQueueEntryList + */ +public class SelfValidatingSortedQueueEntryList extends SortedQueueEntryList +{ + public SelfValidatingSortedQueueEntryList(AMQQueue queue, String propertyName) + { + super(queue, propertyName); + } + + @Override /** Overridden to automatically check queue properties before and after. */ + public SortedQueueEntryImpl add(final ServerMessage message) + { + assertQueueProperties(); //before add + final SortedQueueEntryImpl result = super.add(message); + assertQueueProperties(); //after add + return result; + } + + @Override /** Overridden to automatically check queue properties before and after. */ + public void entryDeleted(SortedQueueEntryImpl entry) + { + assertQueueProperties(); //before delete + super.entryDeleted(entry); + assertQueueProperties(); //after delete + } + + public void assertQueueProperties() + { + assertRootIsBlack(); + assertTreeIntegrity(); + assertChildrenOfRedAreBlack(); + assertLeavesSameBlackPath(); + } + + public void assertRootIsBlack() + { + if(!isNodeColour(getRoot(), Colour.BLACK)) + { + Assert.fail("Root Not Black"); + } + } + + public void assertTreeIntegrity() + { + assertTreeIntegrity(getRoot()); + } + + public void assertTreeIntegrity(final SortedQueueEntryImpl node) + { + if(node == null) + { + return; + } + if(node.getLeft() != null) + { + if(node.getLeft().getParent() == node) + { + assertTreeIntegrity(node.getLeft()); + } + else + { + Assert.fail("Tree integrity compromised"); + } + } + if(node.getRight() != null) + { + if(node.getRight().getParent() == node) + { + assertTreeIntegrity(node.getRight()); + } + else + { + Assert.fail("Tree integrity compromised"); + } + + } + } + + public void assertLeavesSameBlackPath() + { + assertLeavesSameBlackPath(getRoot()); + } + + public int assertLeavesSameBlackPath(final SortedQueueEntryImpl node) + { + if(node == null) + { + return 1; + } + final int left = assertLeavesSameBlackPath(node.getLeft()); + final int right = assertLeavesSameBlackPath(node.getLeft()); + if(left == right) + { + return isNodeColour(node, Colour.BLACK) ? 1 + left : left; + } + else + { + Assert.fail("Unequal paths to leaves"); + return 1; //compiler + } + } + + public void assertChildrenOfRedAreBlack() + { + assertChildrenOfRedAreBlack(getRoot()); + } + + public void assertChildrenOfRedAreBlack(final SortedQueueEntryImpl node) + { + if(node == null) + { + return; + } + else if(node.getColour() == Colour.BLACK) + { + assertChildrenOfRedAreBlack(node.getLeft()); + assertChildrenOfRedAreBlack(node.getRight()); + } + else + { + if(isNodeColour(node.getLeft(), Colour.BLACK) + && isNodeColour(node.getRight(), Colour.BLACK)) + { + assertChildrenOfRedAreBlack(node.getLeft()); + assertChildrenOfRedAreBlack(node.getRight()); + } + else + { + Assert.fail("Children of Red are not both black"); + } + } + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index f4cdbbe02c..6c7094cac0 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -21,8 +21,12 @@ package org.apache.qpid.server.queue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.apache.commons.configuration.PropertiesConfiguration; - import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; @@ -51,12 +55,6 @@ import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - public class SimpleAMQQueueTest extends InternalBrokerBaseCase { @@ -190,6 +188,13 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // Check sending a message ends up with the subscriber AMQMessage messageA = createMessage(new Long(24)); _queue.enqueue(messageA); + try + { + Thread.sleep(2000L); + } + catch(InterruptedException e) + { + } assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage()); assertNull(((QueueContext)_subscription.getQueueContext())._releasedEntry); @@ -431,6 +436,13 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // Check sending a message ends up with the subscriber AMQMessage messageA = createMessage(new Long(24)); _queue.enqueue(messageA); + try + { + Thread.sleep(2000L); + } + catch (InterruptedException e) + { + } assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage()); // Check we cannot add a second subscriber to the queue @@ -724,7 +736,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase assertEquals("No messages should have been delivered yet", 0, sub3.getMessages().size()); // call processQueue to deliver the messages - testQueue.processQueue(new QueueRunner(testQueue, 1) + testQueue.processQueue(new QueueRunner(testQueue) { @Override public void run() @@ -827,7 +839,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not copied as part of invocation of - * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String, StoreContext)} + * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String, ServerTransaction)} */ public void testCopyMessagesWithDequeuedEntry() { @@ -845,7 +857,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase SimpleAMQQueue queue = createQueue(anotherQueueName); // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); // copy messages into another queue _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); @@ -877,7 +889,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not moved as part of invocation of - * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String, StoreContext)} + * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String, ServerTransaction)} */ public void testMovedMessagesWithDequeuedEntry() { @@ -895,7 +907,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase SimpleAMQQueue queue = createQueue(anotherQueueName); // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); // move messages into another queue _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); @@ -928,7 +940,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that messages in given range including dequeued one are deleted * from the queue on invocation of - * {@link SimpleAMQQueue#removeMessagesFromQueue(long, long, StoreContext)} + * {@link SimpleAMQQueue#removeMessagesFromQueue(long, long)} */ public void testRemoveMessagesFromQueueWithDequeuedEntry() { @@ -955,7 +967,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message on the top is not accounted and next message * is deleted from the queue on invocation of - * {@link SimpleAMQQueue#deleteMessageFromTop(StoreContext)} + * {@link SimpleAMQQueue#deleteMessageFromTop()} */ public void testDeleteMessageFromTopWithDequeuedEntryOnTop() { @@ -984,7 +996,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that all messages including dequeued one are deleted from the queue - * on invocation of {@link SimpleAMQQueue#clearQueue(StoreContext)} + * on invocation of {@link SimpleAMQQueue#clearQueue()} */ public void testClearQueueWithDequeuedEntry() { @@ -1050,10 +1062,12 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase { /** * Send a message and decrement latch + * @param entry + * @param batch */ - public void send(QueueEntry msg) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { - super.send(msg); + super.send(entry, batch); latch.countDown(); } }; @@ -1064,7 +1078,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase testQueue.registerSubscription(subscription, false); // process queue - testQueue.processQueue(new QueueRunner(testQueue, 1) + testQueue.processQueue(new QueueRunner(testQueue) { public void run() { @@ -1110,9 +1124,9 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase * Entries with even message id are considered * dequeued! */ - protected QueueEntryImpl createQueueEntry(final ServerMessage message) + protected SimpleQueueEntryImpl createQueueEntry(final ServerMessage message) { - return new QueueEntryImpl(this, message) + return new SimpleQueueEntryImpl(this, message) { public boolean isDequeued() { @@ -1128,6 +1142,19 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase { return !(((AMQMessage) message).getMessageId().longValue() % 2 == 0); } + + @Override + public boolean acquire(Subscription sub) + { + if(((AMQMessage) message).getMessageId().longValue() % 2 == 0) + { + return false; + } + else + { + return super.acquire(sub); + } + } }; } }; @@ -1244,6 +1271,14 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase fail("Failure to put message on queue:" + e.getMessage()); } } + try + { + Thread.sleep(2000L); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } } /** diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java new file mode 100644 index 0000000000..d8d78bbb84 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java @@ -0,0 +1,59 @@ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; + +public class SimpleQueueEntryImplTest extends QueueEntryImplTestBase { + + private SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); + + public QueueEntryImpl getQueueEntryImpl(int msgId) throws AMQException { + ServerMessage message = new MockAMQMessage(msgId); + return queueEntryList.add(message); + } + + public void testCompareTo() + { + assertTrue(_queueEntry.compareTo(_queueEntry2) < 0); + assertTrue(_queueEntry2.compareTo(_queueEntry3) < 0); + assertTrue(_queueEntry.compareTo(_queueEntry3) < 0); + + assertTrue(_queueEntry2.compareTo(_queueEntry) > 0); + assertTrue(_queueEntry3.compareTo(_queueEntry2) > 0); + assertTrue(_queueEntry3.compareTo(_queueEntry) > 0); + + assertTrue(_queueEntry.compareTo(_queueEntry) == 0); + assertTrue(_queueEntry2.compareTo(_queueEntry2) == 0); + assertTrue(_queueEntry3.compareTo(_queueEntry3) == 0); + } + + public void testTraverseWithNoDeletedEntries() + { + QueueEntry current = _queueEntry; + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry2, current); + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry3, current); + + current = current.getNextValidEntry(); + assertNull(current); + + } + + public void testTraverseWithDeletedEntries() + { + // Delete 2nd queue entry + _queueEntry2.delete(); + assertTrue(_queueEntry2.isDeleted()); + + QueueEntry current = _queueEntry; + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry3, current); + + current = current.getNextValidEntry(); + assertNull(current); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java index 7136f07ca5..f3ba6a5495 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java @@ -22,21 +22,28 @@ package org.apache.qpid.server.queue; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.ServerMessage; -import junit.framework.TestCase; - -public class SimpleQueueEntryListTest extends TestCase +public class SimpleQueueEntryListTest extends QueueEntryListTestBase { + private SimpleQueueEntryList _sqel; + private static final String SCAVENGE_PROP = "qpid.queue.scavenge_count"; String oldScavengeValue = null; - + @Override protected void setUp() { oldScavengeValue = System.setProperty(SCAVENGE_PROP, "9"); + _sqel = new SimpleQueueEntryList(_testQueue); + for(int i = 1; i <= 100; i++) + { + final ServerMessage msg = new MockAMQMessage(i); + final QueueEntry bleh = _sqel.add(msg); + assertNotNull("QE should not have been null", bleh); + } } @Override @@ -52,19 +59,28 @@ public class SimpleQueueEntryListTest extends TestCase } } - /** - * Tests the behavior of the next(QueuyEntry) method. - */ - public void testNext() throws Exception + @Override + public QueueEntryList getTestList() { - SimpleQueueEntryList sqel = new SimpleQueueEntryList(null); - int i = 0; + return _sqel; + } + + @Override + public long getExpectedFirstMsgId() + { + return 1; + } - QueueEntry queueEntry1 = sqel.add(new MockAMQMessage(i++)); - QueueEntry queueEntry2 = sqel.add(new MockAMQMessage(i++)); + @Override + public int getExpectedListLength() + { + return 100; + } - assertSame(queueEntry2, sqel.next(queueEntry1)); - assertNull(sqel.next(queueEntry2)); + @Override + public AMQMessage getTestMessageToAdd() throws AMQException + { + return new MockAMQMessage(1l); } public void testScavenge() throws Exception @@ -82,7 +98,7 @@ public class SimpleQueueEntryListTest extends TestCase entriesMap.put(i,bleh); } - QueueEntryImpl head = ((QueueEntryImpl) sqel.getHead()); + SimpleQueueEntryImpl head = sqel.getHead(); //We shall now delete some specific messages mid-queue that will lead to //requiring a scavenge once the requested threshold of 9 deletes is passed @@ -99,11 +115,10 @@ public class SimpleQueueEntryListTest extends TestCase assertTrue("Failed to delete QueueEntry", entriesMap.remove(14).delete()); verifyDeletedButPresentBeforeScavenge(head, 14); - //Delete message 20 only assertTrue("Failed to delete QueueEntry", entriesMap.remove(20).delete()); verifyDeletedButPresentBeforeScavenge(head, 20); - + //Delete messages 81 to 84 assertTrue("Failed to delete QueueEntry", entriesMap.remove(81).delete()); verifyDeletedButPresentBeforeScavenge(head, 81); @@ -113,35 +128,35 @@ public class SimpleQueueEntryListTest extends TestCase verifyDeletedButPresentBeforeScavenge(head, 83); assertTrue("Failed to delete QueueEntry", entriesMap.remove(84).delete()); verifyDeletedButPresentBeforeScavenge(head, 84); - + //Delete message 99 - this is the 10th message deleted that is after the queue head //and so will invoke the scavenge() which is set to go after 9 previous deletions assertTrue("Failed to delete QueueEntry", entriesMap.remove(99).delete()); verifyAllDeletedMessagedNotPresent(head, entriesMap); } - - private void verifyDeletedButPresentBeforeScavenge(QueueEntryImpl head, long messageId) + + private void verifyDeletedButPresentBeforeScavenge(SimpleQueueEntryImpl head, long messageId) { //Use the head to get the initial entry in the queue - QueueEntryImpl entry = head._next; - + SimpleQueueEntryImpl entry = head.getNextNode(); + for(long i = 1; i < messageId ; i++) { assertEquals("Expected QueueEntry was not found in the list", i, (long) entry.getMessage().getMessageNumber()); - entry = entry._next; + entry = entry.getNextNode(); } - + assertTrue("Entry should have been deleted", entry.isDeleted()); } - - private void verifyAllDeletedMessagedNotPresent(QueueEntryImpl head, Map<Integer,QueueEntry> remainingMessages) + + private void verifyAllDeletedMessagedNotPresent(SimpleQueueEntryImpl head, Map<Integer,QueueEntry> remainingMessages) { //Use the head to get the initial entry in the queue - QueueEntryImpl entry = head._next; - + SimpleQueueEntryImpl entry = head.getNextNode(); + assertNotNull("Initial entry should not have been null", entry); - + int count = 0; while (entry != null) @@ -149,62 +164,56 @@ public class SimpleQueueEntryListTest extends TestCase assertFalse("Entry " + entry.getMessage().getMessageNumber() + " should not have been deleted", entry.isDeleted()); assertNotNull("QueueEntry was not found in the list of remaining entries", remainingMessages.get(entry.getMessage().getMessageNumber().intValue())); - + count++; - entry = entry._next; + entry = entry.getNextNode(); } - + assertEquals("Count should have been equal",count,remainingMessages.size()); } - public void testDequedMessagedNotPresentInIterator() + public void testGettingNextElement() { - int numberOfMessages = 10; - SimpleQueueEntryList entryList = new SimpleQueueEntryList(new MockAMQQueue("test")); - QueueEntry[] entries = new QueueEntry[numberOfMessages]; + final int numberOfEntries = 5; + final SimpleQueueEntryImpl[] entries = new SimpleQueueEntryImpl[numberOfEntries]; + final SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); - for(int i = 0; i < numberOfMessages ; i++) + // create test entries + for(int i = 0; i < numberOfEntries; i++) { - AMQMessage message = null;; - try - { - message = new MockAMQMessage(i); - } - catch (AMQException e) - { - fail("Failure to create a mock message:" + e.getMessage()); - } - QueueEntry entry = entryList.add(message); - assertNotNull("QE should not be null", entry); - entries[i]= entry; + AMQMessage message = new MockAMQMessage(i); + entries[i] = queueEntryList.add(message); } - // dequeue all even messages - for (QueueEntry queueEntry : entries) + // test getNext for not acquired entries + for(int i = 0; i < numberOfEntries; i++) { - long i = ((AMQMessage)queueEntry.getMessage()).getMessageId().longValue(); - if (i%2 == 0) + final SimpleQueueEntryImpl next = entries[i].getNextValidEntry(); + + if(i < numberOfEntries - 1) + { + assertEquals("Unexpected entry from QueueEntryImpl#getNext()", entries[i + 1], next); + } + else { - queueEntry.acquire(); - queueEntry.dequeue(); + assertNull("The next entry after the last should be null", next); } } - // iterate and check that dequeued messages are not returned by iterator - QueueEntryIterator it = entryList.iterator(); - int counter = 0; - int i = 1; - while (it.advance()) - { - QueueEntry entry = it.getNode(); - Long id = ((AMQMessage)entry.getMessage()).getMessageId(); - assertEquals("Expected message with id " + i + " but got message with id " - + id, new Long(i), id); - counter++; - i += 2; - } - int expectedNumber = numberOfMessages / 2; - assertEquals("Expected " + expectedNumber + " number of entries in iterator but got " + counter, - expectedNumber, counter); + // delete second + entries[1].acquire(); + entries[1].delete(); + + // dequeue third + entries[2].acquire(); + entries[2].dequeue(); + + SimpleQueueEntryImpl next = entries[2].getNextValidEntry(); + assertEquals("expected forth entry", entries[3], next); + next = next.getNextValidEntry(); + assertEquals("expected fifth entry", entries[4], next); + next = next.getNextValidEntry(); + assertNull("The next entry after the last should be null", next); } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryImplTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryImplTest.java new file mode 100644 index 0000000000..43fb5b4cb3 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryImplTest.java @@ -0,0 +1,62 @@ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; + +public class SortedQueueEntryImplTest extends QueueEntryImplTestBase { + + public final static String keys[] = { "CCC", "AAA", "BBB" }; + + private SelfValidatingSortedQueueEntryList queueEntryList = new SelfValidatingSortedQueueEntryList(new MockAMQQueue("test"),"KEY"); + + public QueueEntryImpl getQueueEntryImpl(int msgId) throws AMQException { + final ServerMessage message = new MockAMQMessage(msgId, "KEY", keys[msgId-1]); + return queueEntryList.add(message); + } + + public void testCompareTo() + { + assertTrue(_queueEntry.compareTo(_queueEntry2) > 0); + assertTrue(_queueEntry.compareTo(_queueEntry3) > 0); + + assertTrue(_queueEntry2.compareTo(_queueEntry3) < 0); + assertTrue(_queueEntry2.compareTo(_queueEntry) < 0); + + assertTrue(_queueEntry3.compareTo(_queueEntry2) > 0); + assertTrue(_queueEntry3.compareTo(_queueEntry) < 0); + + assertTrue(_queueEntry.compareTo(_queueEntry) == 0); + assertTrue(_queueEntry2.compareTo(_queueEntry2) == 0); + assertTrue(_queueEntry3.compareTo(_queueEntry3) == 0); + } + + public void testTraverseWithNoDeletedEntries() + { + QueueEntry current = _queueEntry2; + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry3, current); + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry, current); + + current = current.getNextValidEntry(); + assertNull(current); + + } + + public void testTraverseWithDeletedEntries() + { + // Delete 2nd queue entry + _queueEntry3.delete(); + assertTrue(_queueEntry3.isDeleted()); + + QueueEntry current = _queueEntry2; + + current = current.getNextValidEntry(); + assertSame("Unexpected current entry",_queueEntry, current); + + current = current.getNextValidEntry(); + assertNull(current); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java new file mode 100644 index 0000000000..eca845644e --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java @@ -0,0 +1,323 @@ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.message.AMQMessage; + +import java.util.Arrays; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; + +public class SortedQueueEntryListTest extends QueueEntryListTestBase +{ + private static SelfValidatingSortedQueueEntryList _sqel; + + public final static String keys[] = { " 73", " 18", " 11", "127", "166", "163", " 69", " 60", "191", "144", + " 17", "161", "145", "140", "157", " 47", "136", " 56", "176", " 81", + "195", " 96", " 2", " 68", "101", "141", "159", "187", "149", " 45", + " 64", "100", " 83", " 51", " 79", " 82", "180", " 26", " 61", " 62", + " 78", " 46", "147", " 91", "120", "164", " 92", "172", "188", " 50", + "111", " 89", " 4", " 8", " 16", "151", "122", "178", " 33", "124", + "171", "165", "116", "113", "155", "148", " 29", " 0", " 37", "131", + "146", " 57", "112", " 97", " 23", "108", "123", "117", "167", " 52", + " 98", " 6", "160", " 25", " 49", " 34", "182", "185", " 30", " 66", + "152", " 58", " 86", "118", "189", " 84", " 36", "104", " 7", " 76", + " 87", " 1", " 80", " 10", "142", " 59", "137", " 12", " 67", " 22", + " 9", "106", " 75", "109", " 93", " 42", "177", "134", " 77", " 88", + "114", " 43", "143", "135", " 55", "181", " 32", "174", "175", "184", + "133", "107", " 28", "126", "103", " 85", " 38", "158", " 39", "162", + "129", "194", " 15", " 24", " 19", " 35", "186", " 31", " 65", " 99", + "192", " 74", "156", " 27", " 95", " 54", " 70", " 13", "110", " 41", + " 90", "173", "125", "196", "130", "183", "102", "190", "132", "105", + " 21", " 53", "139", " 94", "115", " 48", " 44", "179", "128", " 14", + " 72", "119", "153", "168", "197", " 40", "150", "138", " 5", "154", + "169", " 71", "199", "198", "170", " 3", "121", " 20", " 63", "193" }; + + public final static String textkeys[] = { "AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ", + "KKK", "LLL", "MMM", "NNN", "OOO", "PPP", "QQQ", "RRR", "SSS", "TTT", + "UUU", "VVV", "XXX", "YYY", "ZZZ"}; + + private final static String keysSorted[] = keys.clone(); + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + // Create result array + Arrays.sort(keysSorted); + + // Create test list + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + // Build test list + long messageId = 0L; + for(final String key : keys) + { + final ServerMessage msg = generateTestMessage(messageId++, key); + _sqel.add(msg); + } + + } + + public QueueEntryList getTestList() + { + return _sqel; + } + + public int getExpectedListLength() + { + return keys.length; + } + + public long getExpectedFirstMsgId() + { + return 67L; + } + + public ServerMessage getTestMessageToAdd() throws AMQException + { + return generateTestMessage(1, "test value"); + } + + private ServerMessage generateTestMessage(final long id, final String keyValue) throws AMQException + { + return new AMQMessage(new MockStoredMessage(id, "KEY", keyValue)); + } + + public void testIterator() + { + super.testIterator(); + + // Test sorted order of list + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count = 0; + while(iter.advance()) + { + assertEquals("Sorted queue entry value does not match sorted key array", + keysSorted[count++], getSortedKeyValue(iter)); + } + } + + private Object getSortedKeyValue(QueueEntryIterator<?> iter) + { + return ((SortedQueueEntryImpl) iter.getNode()).getMessage().getMessageHeader().getHeader("KEY"); + } + + private Long getMessageId(QueueEntryIterator<?> iter) + { + return ((SortedQueueEntryImpl) iter.getNode()).getMessage().getMessageNumber(); + } + + public void testNonUniqueSortKeys() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + // Build test list + long messageId = 0L; + while(messageId < 200) + { + final ServerMessage msg = generateTestMessage(messageId++, "samekey"); + _sqel.add(msg); + } + + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count=0; + while(iter.advance()) + { + assertEquals("Sorted queue entry value is not as expected", "samekey", getSortedKeyValue(iter)); + assertEquals("Message id not as expected", Long.valueOf(count++), getMessageId(iter)); + } + } + + public void testNullSortKeys() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + // Build test list + long messageId = 0L; + while(messageId < 200) + { + final ServerMessage msg = generateTestMessage(messageId++, null); + _sqel.add(msg); + } + + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count=0; + while(iter.advance()) + { + assertNull("Sorted queue entry value is not as expected", getSortedKeyValue(iter)); + assertEquals("Message id not as expected", Long.valueOf(count++), getMessageId(iter)); } + } + + public void testAscendingSortKeys() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + // Build test list + long messageId = 0L; + for(String textKey : textkeys) + { + final ServerMessage msg = generateTestMessage(messageId, textKey); + messageId++; + _sqel.add(msg); + } + + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count=0; + while(iter.advance()) + { + assertEquals("Sorted queue entry value is not as expected", textkeys[count], getSortedKeyValue(iter)); + assertEquals("Message id not as expected", Long.valueOf(count), getMessageId(iter)); + count++; + } + } + + public void testDescendingSortKeys() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + // Build test list + long messageId = 0L; + for(int i=textkeys.length-1; i >=0; i--) + { + final ServerMessage msg = generateTestMessage(messageId, textkeys[i]); + messageId++; + _sqel.add(msg); + } + + final QueueEntryIterator<?> iter = getTestList().iterator(); + int count=0; + while(iter.advance()) + { + assertEquals("Sorted queue entry value is not as expected", textkeys[count], getSortedKeyValue(iter)); + assertEquals("Message id not as expected", Long.valueOf(textkeys.length-count-1), getMessageId(iter)); + count++; + } + } + + public void testInsertAfter() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + ServerMessage msg = generateTestMessage(1, "A"); + _sqel.add(msg); + + SortedQueueEntryImpl entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 1); + + msg = generateTestMessage(2, "B"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "B", 2); + } + + public void testInsertBefore() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + ServerMessage msg = generateTestMessage(1, "B"); + _sqel.add(msg); + + SortedQueueEntryImpl entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "B", 1); + + msg = generateTestMessage(2, "A"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 2); + + entry = _sqel.next(entry); + validateEntry(entry, "B", 1); + } + + public void testInsertInbetween() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + ServerMessage msg = generateTestMessage(1, "A"); + _sqel.add(msg); + SortedQueueEntryImpl entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 1); + + msg = generateTestMessage(2, "C"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "C", 2); + + msg = generateTestMessage(3, "B"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "B", 3); + + entry = _sqel.next(entry); + validateEntry(entry, "C", 2); + } + + public void testInsertAtHead() throws Exception + { + _sqel = new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + + ServerMessage msg = generateTestMessage(1, "B"); + _sqel.add(msg); + + SortedQueueEntryImpl entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "B", 1); + + msg = generateTestMessage(2, "D"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "B", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "D", 2); + + msg = generateTestMessage(3, "C"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "B", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "C", 3); + + entry = _sqel.next(entry); + validateEntry(entry, "D", 2); + + msg = generateTestMessage(4, "A"); + _sqel.add(msg); + + entry = _sqel.next(_sqel.getHead()); + validateEntry(entry, "A", 4); + + entry = _sqel.next(entry); + validateEntry(entry, "B", 1); + + entry = _sqel.next(entry); + validateEntry(entry, "C", 3); + + entry = _sqel.next(entry); + validateEntry(entry, "D", 2); + } + + private void validateEntry(final SortedQueueEntryImpl entry, final String expectedSortKey, final long expectedMessageId) + { + assertEquals("Sorted queue entry value is not as expected", + expectedSortKey, entry.getMessage().getMessageHeader().getHeader("KEY")); + assertEquals("Sorted queue entry id is not as expected", + Long.valueOf(expectedMessageId), entry.getMessage().getMessageNumber()); + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java deleted file mode 100644 index 6ca88d1796..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * 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.qpid.server.store; - -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.protocol.InternalTestProtocolSession; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; - -import java.util.List; - -public class MessageStoreShutdownTest extends InternalBrokerBaseCase -{ - - public void test() - { - subscribe(getSession(), getChannel(), getQueue()); - - try - { - publishMessages(getSession(), getChannel(), 1); - } - catch (AMQException e) - { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - fail(e.getMessage()); - } - - try - { - getRegistry().close(); - } - catch (Exception e) - { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - fail(e.getMessage()); - } - - assertTrue("Session should now be closed", getSession().isClosed()); - - - //Test attempting to modify the broker state after session has been closed. - - //The Message should have been removed from the unacked list. - - //Ack Messages - List<InternalTestProtocolSession.DeliveryPair> list = getSession().getDelivers(getChannel().getChannelId(), new AMQShortString("sgen_1"), 1); - - InternalTestProtocolSession.DeliveryPair pair = list.get(0); - - try - { - // The message should now be requeued and so unable to ack it. - getChannel().acknowledgeMessage(pair.getDeliveryTag(), false); - } - catch (AMQException e) - { - assertEquals("Incorrect exception thrown", "Single ack on delivery tag 1 not known for channel:1", e.getMessage()); - } - - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java index 3acd064fd7..1d0a9d6316 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -689,7 +689,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase if (usePriority) { queueArguments = new FieldTable(); - queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + queueArguments.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), DEFAULT_PRIORTY_LEVEL); } if (lastValueQueue) @@ -767,7 +767,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase private void bindAllQueuesToExchange(Exchange exchange, AMQShortString routingKey) { FieldTable queueArguments = new FieldTable(); - queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + queueArguments.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), DEFAULT_PRIORTY_LEVEL); QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry(); @@ -781,7 +781,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase private void bindAllTopicQueuesToExchange(Exchange exchange, AMQShortString routingKey) { FieldTable queueArguments = new FieldTable(); - queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + queueArguments.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), DEFAULT_PRIORTY_LEVEL); QueueRegistry queueRegistry = getVirtualHost().getQueueRegistry(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java index be3e1228a6..4a74596d02 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -125,6 +125,12 @@ public class MockSubscription implements Subscription return queue; } + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); + } + + public void getSendLock() { _stateChangeLock.lock(); @@ -202,7 +208,11 @@ public class MockSubscription implements Subscription { } - public void send(QueueEntry entry) throws AMQException + public void releaseQueueEntry(QueueEntry queueEntry) + { + } + + public void send(QueueEntry entry, boolean batch) throws AMQException { if (messages.contains(entry)) { @@ -211,6 +221,12 @@ public class MockSubscription implements Subscription messages.add(entry); } + @Override + public void flushBatched() + { + + } + public void setQueueContext(AMQQueue.Context queueContext) { _queueContext = queueContext; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java new file mode 100644 index 0000000000..78ba8c1645 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java @@ -0,0 +1,229 @@ +/* + * + * 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.qpid.server.transport; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.server.configuration.MockConnectionConfig; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Binary; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.Session; + +public class ServerConnectionMBeanTest extends InternalBrokerBaseCase +{ + private ServerConnection _serverConnection; + private ServerSessionMock _serverSession; + private ServerConnectionMBean _mbean; + private List<Session> _sessions = new ArrayList<Session>(); + + @Override + public void setUp() throws Exception + { + super.setUp(); + + final VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); + _serverConnection = new ServerConnection(1) + { + protected Collection<Session> getChannels() + { + return _sessions; + } + public Session getSession(int channelId) + { + for(Session session : _sessions) + { + if (session.getChannel() == channelId) + { + return session; + } + } + return null; + } + @Override + public AtomicLong getLastIoTime() + { + return new AtomicLong(1); + } + }; + final MockConnectionConfig config = new MockConnectionConfig(UUID.randomUUID(), null, null, + false, 1, vhost, "address", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, + "authid", "remoteProcessName", new Integer(1967), new Integer(1970), vhost.getConfigStore(), Boolean.FALSE); + _serverConnection.setConnectionConfig(config); + _serverConnection.setVirtualHost(vhost); + _serverConnection.setConnectionDelegate(new ServerConnectionDelegate(getRegistry(), "")); + _serverSession = new ServerSessionMock(_serverConnection, 1); + _mbean = (ServerConnectionMBean) _serverConnection.getManagedObject(); + } + + public void testChannels() throws Exception + { + // check the channel count is correct + TabularData tabularData = _mbean.channels(); + + int channelCount = tabularData.size(); + assertEquals("Unexpected number of channels",1,channelCount); + _sessions.add(new ServerSession(_serverConnection, new ServerSessionDelegate(), + new Binary(getName().getBytes()), 2 , _serverConnection.getConfig())); + + channelCount = _mbean.channels().size(); + assertEquals("Unexpected number of channels",2,channelCount); + + final CompositeData chanresult = tabularData.get(new Integer[]{1}); + assertNotNull(chanresult); + assertEquals("Unexpected channel id", new Integer(1),(Integer)chanresult.get(ManagedConnection.CHAN_ID)); + assertNull("Unexpected default queue", chanresult.get(ManagedConnection.DEFAULT_QUEUE)); + assertFalse("Unexpected transactional flag", (Boolean)chanresult.get(ManagedConnection.TRANSACTIONAL)); + assertFalse("Flow should have been blocked", (Boolean)chanresult.get(ManagedConnection.FLOW_BLOCKED)); + assertEquals("Unexpected unack'd count", new Integer(1967), (Integer)chanresult.get(ManagedConnection.UNACKED_COUNT)); + } + + public void testMaxChannels() throws Exception + { + _serverConnection.getConnectionDelegate().setChannelMax(10001); + assertEquals("Max channels not got correctly", new Long(10001), _mbean.getMaximumNumberOfChannels()); + } + + public void testRollback() throws Exception + { + _mbean.rollbackTransactions(1); + assertFalse("Rollback performed despite not being transacted", _serverSession.isRolledback()); + + _serverSession.setTransactional(true); + _mbean.rollbackTransactions(1); + assertTrue("Rollback not performed", _serverSession.isRolledback()); + + try + { + _mbean.rollbackTransactions(2); + fail("Exception expected"); + } + catch (JMException jme) + { + //pass + } + } + + public void testCommit() throws Exception + { + _mbean.commitTransactions(1); + assertFalse("Commit performed despite not being transacted", _serverSession.isCommitted()); + + _serverSession.setTransactional(true); + _mbean.commitTransactions(1); + assertTrue("Commit not performed", _serverSession.isCommitted()); + + try + { + _mbean.commitTransactions(2); + fail("Exception expected"); + } + catch (JMException jme) + { + //pass + } + } + + public void testGetName() + { + assertEquals("Unexpected Object Instance Name", "\"address\"", _mbean.getObjectInstanceName()); + } + + public void testEnableStatistics() + { + assertFalse("Unexpected statistics enable flag", _mbean.isStatisticsEnabled()); + _mbean.setStatisticsEnabled(true); + assertTrue("Unexpected statistics enable flag", _mbean.isStatisticsEnabled()); + } + + public void testLastIOTime() + { + assertEquals("Unexpected last IO time", new Date(1), _mbean.getLastIoTime()); + } + + private class ServerSessionMock extends ServerSession + { + private int _channelId = 0; + private boolean _committed = false; + private boolean _rolledback = false; + private boolean _transacted = false; + + ServerSessionMock(Connection connection, int channelId) + { + super(connection, new ServerSessionDelegate(), new Binary(String.valueOf(channelId).getBytes()), 1 , _serverConnection.getConfig()); + _channelId = channelId; + _sessions.add(this); + } + + public int getChannel() + { + return _channelId; + } + + @Override + public void commit() + { + _committed = true; + } + + @Override + public void rollback() + { + _rolledback = true; + } + + public boolean isCommitted() + { + return _committed; + } + + public boolean isRolledback() + { + return _rolledback; + } + + @Override + public int getUnacknowledgedMessageCount() + { + return 1967; + } + + public boolean isTransactional() + { + return _transacted; + } + + public void setTransactional(boolean transacted) + { + _transacted = transacted; + } + } +} diff --git a/qpid/java/build.deps b/qpid/java/build.deps index 17f963ece7..db28b33b21 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -29,6 +29,10 @@ commons-logging=lib/commons-logging-1.0.4.jar derby-db=lib/derby-10.6.1.0.jar geronimo-jms=lib/geronimo-jms_1.1_spec-1.0.jar +geronimo-j2ee=lib/geronimo-j2ee-connector_1.5_spec-2.0.0.jar +geronimo-jta=lib/geronimo-jta_1.1_spec-1.1.1.jar +geronimo-kernel=lib/geronimo-kernel-2.2.1.jar +geronimo-openejb=lib/geronimo-ejb_3.0_spec-1.0.1.jar junit=lib/junit-3.8.1.jar @@ -138,7 +142,11 @@ broker-plugins-experimental-info.test.libs=${test.libs} ${servlet-api} ${jetty} management-eclipse-plugin.test.libs=${test.libs} management-common.test.libs=${test.libs} +# JCA Resource adapter +ra.libs=${geronimo-j2ee} ${geronimo-jta} ${geronimo-jms} ${slf4j-api} ${geronimo-kernel} ${geronimo-openejb} + # optional bdbstore module deps bdb-je=lib/bdbstore/je-4.0.117.jar bdbstore.libs=${bdb-je} bdbstore.test.libs=${test.libs} + diff --git a/qpid/java/build.xml b/qpid/java/build.xml index e6c154d3d0..1f7de49df6 100644 --- a/qpid/java/build.xml +++ b/qpid/java/build.xml @@ -32,8 +32,9 @@ <property name="modules.management" value="${management}"/> <property name="modules.plugin" value="${broker-plugins} ${client-plugins}"/> <property name="modules.opt" value=""/> + <property name="modules.jca" value="jca"/> <property name="modules" value="${modules.core} ${modules.examples} - ${modules.management} ${modules.tests} ${modules.plugin} ${modules.opt}"/> + ${modules.management} ${modules.tests} ${modules.plugin} ${modules.opt} ${modules.jca}"/> <property name="qpid.jar" location="${build.lib}/qpid-all.jar"/> <basename property="qpid.jar.name" file="${qpid.jar}"/> diff --git a/qpid/java/client-plugins/.gitignore b/qpid/java/client-plugins/.gitignore index e69de29bb2..e5f63e731b 100644 --- a/qpid/java/client-plugins/.gitignore +++ b/qpid/java/client-plugins/.gitignore @@ -0,0 +1,21 @@ +# +# 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. +# + +# Holder file to ensure git-svn creates this otherwise empty +# directory, necessary for supporting older Ant versions. diff --git a/qpid/java/client/src/main/java/client.bnd b/qpid/java/client/src/main/java/client.bnd index bd13844e8e..d92d582ec8 100755 --- a/qpid/java/client/src/main/java/client.bnd +++ b/qpid/java/client/src/main/java/client.bnd @@ -23,4 +23,5 @@ Bundle-SymbolicName: qpid-client Bundle-Version: ${ver} Export-Package: *;version=${ver} Bundle-RequiredExecutionEnvironment: J2SE-1.5 +DynamicImport-Package: * diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java index c0d4d8a893..b343820d80 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java @@ -264,12 +264,30 @@ public class AMQBrokerDetails implements BrokerDetails public boolean getBooleanProperty(String propName) { - if (_options.containsKey(propName)) - { - return Boolean.parseBoolean(_options.get(propName)); - } - - return false; + return getBooleanProperty(propName, false); + } + + public boolean getBooleanProperty(String propName, boolean defaultValue) + { + if (_options.containsKey(propName)) + { + if (_options.get(propName).equalsIgnoreCase("false")) + { + return false; + } + else if (_options.get(propName).equalsIgnoreCase("true")) + { + return true; + } + else + { + return defaultValue; + } + } + else + { + return defaultValue; + } } public void setTimeout(long timeout) @@ -439,7 +457,7 @@ public class AMQBrokerDetails implements BrokerDetails if (getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY) != null) { conSettings.setTcpNodelay( - getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)); + getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY,true)); } return conSettings; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java index ad7885f195..6879fe0cfd 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -284,7 +284,10 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } String amqpVersion = System.getProperty((ClientProperties.AMQP_VERSION), "0-10"); - _logger.debug("AMQP version " + amqpVersion); + if (_logger.isDebugEnabled()) + { + _logger.debug("AMQP version " + amqpVersion); + } _failoverPolicy = new FailoverPolicy(connectionURL, this); BrokerDetails brokerDetails = _failoverPolicy.getCurrentBrokerDetails(); @@ -1485,4 +1488,5 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect { return _lastFailoverTime; } + } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java index afb0e45f7a..7fc1d25c18 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java @@ -50,6 +50,8 @@ public interface AMQConnectionDelegate XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException; + XASession createXASession(int ackMode) throws JMSException; + void failoverPrep(); void resubscribeSessions() throws JMSException, AMQException, FailoverException; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java index 5e4f84ce9f..802cc55b0e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -74,12 +74,6 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec org.apache.qpid.transport.Connection _qpidConnection; private ConnectionException exception = null; - static - { - // Register any configured SASL client factories. - org.apache.qpid.client.security.DynamicSaslRegistrar.registerSaslProviders(); - } - //--- constructor public AMQConnectionDelegate_0_10(AMQConnection conn) { @@ -170,6 +164,35 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec return session; } + @Override + public XASession createXASession(int ackMode) + throws JMSException + { + + _conn.checkNotClosed(); + + if (_conn.channelLimitReached()) + { + throw new ChannelLimitReachedException(_conn.getMaximumChannelCount()); + } + + int channelId = _conn.getNextChannelID(); + XASessionImpl session; + try + { + session = new XASessionImpl(_qpidConnection, _conn, channelId, ackMode, (int)_conn.getMaxPrefetch(), (int)_conn.getMaxPrefetch() / 2); + _conn.registerSession(channelId, session); + if (_conn._started) + { + session.start(); + } + } + catch (Exception e) + { + throw new JMSAMQException("cannot create session", e); + } + return session; + } /** * Make a connection with the broker diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java index bff4df0e93..399534e834 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java @@ -161,8 +161,8 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate _conn._failoverPolicy.attainedConnection(); _conn._connected = true; return null; - } - else + } + else { return _conn._protocolHandler.getSuggestedProtocolVersion(); } @@ -175,11 +175,16 @@ public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate return createSession(transacted, acknowledgeMode, prefetch, prefetch); } + public XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException { throw new UnsupportedOperationException("0_8 version does not provide XA support"); } + public XASession createXASession(int ackMode) throws JMSException + { + throw new UnsupportedOperationException("0_8 version does not provide XA support"); + } public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetchHigh, final int prefetchLow) throws JMSException { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java index f0c003e02a..700073488e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java @@ -46,6 +46,12 @@ public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionF { private final ConnectionURL _connectionDetails; + // The default constructor is necessary to allow AMQConnectionFactory to be deserialised from JNDI + public AMQConnectionFactory() + { + _connectionDetails = null; + } + public AMQConnectionFactory(final String url) throws URLSyntaxException { if (url == null) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java index f9f50d9150..ee55eb9ce9 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java @@ -34,7 +34,6 @@ import org.apache.qpid.url.URLSyntaxException; public class AMQConnectionURL implements ConnectionURL { - private String _url; private String _failoverMethod; private Map<String, String> _failoverOptions; @@ -279,15 +278,6 @@ public class AMQConnectionURL implements ConnectionURL sb.append(URLHelper.printOptions(_failoverOptions)); sb.append("'"); } - - for (String key : _options.keySet()) - { - if (!key.equals(OPTIONS_FAILOVER) || !key.equals(OPTIONS_BROKERLIST)) - { - sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR).append(key).append("='"); - sb.append(_options.get(key)).append("'"); - } - } return sb.toString(); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java index f9a38138ba..1df809c67c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -75,6 +75,8 @@ public abstract class AMQDestination implements Destination, Referenceable private boolean _exchangeExistsChecked; + private RejectBehaviour _rejectBehaviour; + public static final int QUEUE_TYPE = 1; public static final int TOPIC_TYPE = 2; public static final int UNKNOWN_TYPE = 3; @@ -227,6 +229,8 @@ public abstract class AMQDestination implements Destination, Referenceable _queueName = binding.getQueueName() == null ? null : binding.getQueueName(); _routingKey = binding.getRoutingKey() == null ? null : binding.getRoutingKey(); _bindingKeys = binding.getBindingKeys() == null || binding.getBindingKeys().length == 0 ? new AMQShortString[0] : binding.getBindingKeys(); + final String rejectBehaviourValue = binding.getOption(BindingURL.OPTION_REJECT_BEHAVIOUR); + _rejectBehaviour = rejectBehaviourValue == null ? null : RejectBehaviour.valueOf(rejectBehaviourValue.toUpperCase()); } protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, AMQShortString queueName) @@ -294,7 +298,7 @@ public abstract class AMQDestination implements Destination, Referenceable _bindingKeys = bindingKeys == null || bindingKeys.length == 0 ? new AMQShortString[0] : bindingKeys; _destSyntax = DestSyntax.BURL; _browseOnly = browseOnly; - + _rejectBehaviour = null; if (_logger.isDebugEnabled()) { _logger.debug("Based on " + toString() + " the selected destination syntax is " + _destSyntax); @@ -499,6 +503,13 @@ public abstract class AMQDestination implements Destination, Referenceable sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); } + if (_rejectBehaviour != null) + { + sb.append(BindingURL.OPTION_REJECT_BEHAVIOUR); + sb.append("='" + _rejectBehaviour + "'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + //removeKey the last char '?' if there is no options , ',' if there are. sb.deleteCharAt(sb.length() - 1); url = sb.toString(); @@ -842,4 +853,19 @@ public abstract class AMQDestination implements Destination, Referenceable { return _addressResolved.get() > time; } + + /** + * This option is only applicable for 0-8/0-9/0-9-1 protocols connection + * <p> + * It tells the client to delegate the requeue/DLQ decision to the + * server .If this option is not specified, the messages won't be moved to + * the DLQ (or dropped) when delivery count exceeds the maximum. + * + * @return destination reject behaviour + */ + public RejectBehaviour getRejectBehaviour() + { + return _rejectBehaviour; + } + } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java index d96544adf8..3f9eadeef3 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java @@ -30,6 +30,7 @@ import javax.jms.Queue; import javax.jms.QueueBrowser; import java.util.ArrayList; import java.util.Enumeration; +import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicBoolean; public class AMQQueueBrowser implements QueueBrowser @@ -112,9 +113,12 @@ public class AMQQueueBrowser implements QueueBrowser public QueueBrowserEnumeration(BasicMessageConsumer consumer) throws JMSException { - _nextMessage = consumer == null ? null : consumer.receiveBrowse(); + if (consumer != null) + { + _consumer = consumer; + prefetchMessage(); + } _logger.info("QB:created with first element:" + _nextMessage); - _consumer = consumer; } public boolean hasMoreElements() @@ -126,18 +130,46 @@ public class AMQQueueBrowser implements QueueBrowser public Object nextElement() { Message msg = _nextMessage; + if (msg == null) + { + throw new NoSuchElementException("No messages") ; + } try { _logger.info("QB:nextElement about to receive"); - _nextMessage = _consumer.receiveBrowse(); + prefetchMessage(); _logger.info("QB:nextElement received:" + _nextMessage); } catch (JMSException e) { _logger.warn("Exception caught while queue browsing", e); _nextMessage = null; + try + { + closeConsumer() ; + } + catch (final JMSException jmse) {} // ignore } return msg; } - } + + private void prefetchMessage() throws JMSException + { + _nextMessage = _consumer.receiveBrowse(); + if (_nextMessage == null) + { + closeConsumer() ; + } + } + + private void closeConsumer() throws JMSException + { + if (_consumer != null) + { + BasicMessageConsumer consumer = _consumer ; + _consumer = null ; + consumer.close() ; + } + } + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index ef44221ec1..8984b7ca8c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -310,7 +310,10 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /** Holds the highest received delivery tag. */ protected final AtomicLong _highestDeliveryTag = new AtomicLong(-1); private final AtomicLong _rollbackMark = new AtomicLong(-1); - + + /** Pre-fetched message tags */ + protected ConcurrentLinkedQueue<Long> _prefetchedMessageTags = new ConcurrentLinkedQueue<Long>(); + /** All the not yet acknowledged message tags */ protected ConcurrentLinkedQueue<Long> _unacknowledgedMessageTags = new ConcurrentLinkedQueue<Long>(); @@ -2925,11 +2928,6 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _producers.put(new Long(producerId), producer); } - private void rejectAllMessages(boolean requeue) - { - rejectMessagesForConsumerTag(0, requeue, true); - } - /** * @param consumerTag The consumerTag to prune from queue or all if null * @param requeue Should the removed messages be requeued (or discarded. Possibly to DLQ) @@ -3235,7 +3233,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic for (C consumer : _consumers.values()) { List<Long> tags = consumer.drainReceiverQueueAndRetrieveDeliveryTags(); - _unacknowledgedMessageTags.addAll(tags); + _prefetchedMessageTags.addAll(tags); } setConnectionStopped(isStopped); @@ -3345,7 +3343,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic } else if (_usingDispatcherForCleanup) { - _unacknowledgedMessageTags.add(deliveryTag); + _prefetchedMessageTags.add(deliveryTag); } else { @@ -3548,4 +3546,5 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic _logger.debug("Rollback mark is set to " + _rollbackMark.get()); } } + } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java index 7e5edef38d..756b5cacb0 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -27,11 +27,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; import javax.jms.Destination; import javax.jms.JMSException; @@ -47,8 +50,8 @@ import org.apache.qpid.client.message.AMQMessageDelegateFactory; import org.apache.qpid.client.message.FieldTableSupport; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage_0_10; +import org.apache.qpid.client.messaging.address.AddressHelper; import org.apache.qpid.client.messaging.address.Link; -import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; import org.apache.qpid.client.protocol.AMQProtocolHandler; @@ -142,9 +145,9 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic private int unackedCount = 0; /** - * USed to store the range of in tx messages + * Used to store the range of in tx messages */ - private RangeSet _txRangeSet = new RangeSet(); + private final RangeSet _txRangeSet = new RangeSet(); private int _txSize = 0; //--- constructors @@ -457,18 +460,33 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic public void sendRecover() throws AMQException, FailoverException { // release all unacked messages - RangeSet ranges = gatherUnackedRangeSet(); - getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + RangeSet all = new RangeSet(); + RangeSet delivered = gatherRangeSet(_unacknowledgedMessageTags); + RangeSet prefetched = gatherRangeSet(_prefetchedMessageTags); + for (Iterator<Range> deliveredIter = delivered.iterator(); deliveredIter.hasNext();) + { + Range range = deliveredIter.next(); + all.add(range); + } + for (Iterator<Range> prefetchedIter = prefetched.iterator(); prefetchedIter.hasNext();) + { + Range range = prefetchedIter.next(); + all.add(range); + } + flushProcessed(all, false); + getQpidSession().messageRelease(delivered, Option.SET_REDELIVERED); + getQpidSession().messageRelease(prefetched); + // We need to sync so that we get notify of an error. sync(); } - private RangeSet gatherUnackedRangeSet() + private RangeSet gatherRangeSet(ConcurrentLinkedQueue<Long> messageTags) { RangeSet ranges = new RangeSet(); while (true) { - Long tag = _unacknowledgedMessageTags.poll(); + Long tag = messageTags.poll(); if (tag == null) { break; @@ -480,12 +498,15 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic return ranges; } - public void releaseForRollback() { - getQpidSession().messageRelease(_txRangeSet, Option.SET_REDELIVERED); - _txRangeSet.clear(); - _txSize = 0; + if (_txSize > 0) + { + flushProcessed(_txRangeSet, false); + getQpidSession().messageRelease(_txRangeSet, Option.SET_REDELIVERED); + _txRangeSet.clear(); + _txSize = 0; + } } /** @@ -499,7 +520,15 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic // The value of requeue is always true RangeSet ranges = new RangeSet(); ranges.add((int) deliveryTag); - getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + flushProcessed(ranges, false); + if (requeue) + { + getQpidSession().messageRelease(ranges); + } + else + { + getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + } //I don't think we need to sync } @@ -737,7 +766,7 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic Map<String,Object> arguments = new HashMap<String,Object>(); if (noLocal) { - arguments.put("no-local", true); + arguments.put(AddressHelper.NO_LOCAL, true); } getQpidSession().queueDeclare(queueName.toString(), "" , arguments, @@ -1316,11 +1345,11 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic protected void acknowledgeImpl() { - RangeSet range = gatherUnackedRangeSet(); + RangeSet ranges = gatherRangeSet(_unacknowledgedMessageTags); - if(range.size() > 0 ) + if(ranges.size() > 0 ) { - messageAcknowledge(range, true); + messageAcknowledge(ranges, true); getQpidSession().sync(); } } @@ -1333,6 +1362,13 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic // messages sent by the brokers following the first rollback // after failover _highestDeliveryTag.set(-1); + // Clear txRangeSet/unacknowledgedMessageTags so we don't complete commands corresponding to + //messages that came from the old broker. + _txRangeSet.clear(); + _txSize = 0; + _unacknowledgedMessageTags.clear(); + _prefetchedMessageTags.clear(); super.resubscribe(); + getQpidSession().sync(); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java index e33410f5fe..96df463481 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java @@ -21,6 +21,7 @@ package org.apache.qpid.client; +import java.util.ArrayList; import java.util.Map; import javax.jms.Destination; @@ -40,7 +41,6 @@ import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQState; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; -import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.filter.MessageFilter; import org.apache.qpid.framing.AMQFrame; import org.apache.qpid.framing.AMQMethodBody; @@ -62,7 +62,6 @@ import org.apache.qpid.framing.ExchangeBoundOkBody; import org.apache.qpid.framing.ExchangeDeclareBody; import org.apache.qpid.framing.ExchangeDeclareOkBody; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.framing.QueueBindOkBody; import org.apache.qpid.framing.QueueDeclareBody; @@ -223,6 +222,8 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B public void sendRecover() throws AMQException, FailoverException { + enforceRejectBehaviourDuringRecover(); + _prefetchedMessageTags.clear(); _unacknowledgedMessageTags.clear(); if (isStrictAMQP()) @@ -259,6 +260,49 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B } } + private void enforceRejectBehaviourDuringRecover() + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Prefetched message: _unacknowledgedMessageTags :" + _unacknowledgedMessageTags); + } + ArrayList<BasicMessageConsumer_0_8> consumersToCheck = new ArrayList<BasicMessageConsumer_0_8>(_consumers.values()); + boolean messageListenerFound = false; + boolean serverRejectBehaviourFound = false; + for(BasicMessageConsumer_0_8 consumer : consumersToCheck) + { + if (consumer.isMessageListenerSet()) + { + messageListenerFound = true; + } + if (RejectBehaviour.SERVER.equals(consumer.getRejectBehaviour())) + { + serverRejectBehaviourFound = true; + } + } + _logger.debug("about to pre-reject messages for " + consumersToCheck.size() + " consumer(s)"); + + if (serverRejectBehaviourFound) + { + //reject(false) any messages we don't want returned again + switch(_acknowledgeMode) + { + case Session.DUPS_OK_ACKNOWLEDGE: + case Session.AUTO_ACKNOWLEDGE: + if (!messageListenerFound) + { + break; + } + case Session.CLIENT_ACKNOWLEDGE: + for(Long tag : _unacknowledgedMessageTags) + { + rejectMessage(tag, false); + } + break; + } + } + } + public void releaseForRollback() { // Reject all the messages that have been received in this session and @@ -267,6 +311,17 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B // Otherwise messages will be able to arrive out of order to a second // consumer on the queue. Whilst this is within the JMS spec it is not // user friendly and avoidable. + boolean normalRejectBehaviour = true; + for (BasicMessageConsumer_0_8 consumer : _consumers.values()) + { + if(RejectBehaviour.SERVER.equals(consumer.getRejectBehaviour())) + { + normalRejectBehaviour = false; + //no need to consult other consumers now, found server behaviour. + break; + } + } + while (true) { Long tag = _deliveredMessageTags.poll(); @@ -275,13 +330,14 @@ public final class AMQSession_0_8 extends AMQSession<BasicMessageConsumer_0_8, B break; } - rejectMessage(tag, true); + rejectMessage(tag, normalRejectBehaviour); } } public void rejectMessage(long deliveryTag, boolean requeue) { - if ((_acknowledgeMode == CLIENT_ACKNOWLEDGE) || (_acknowledgeMode == SESSION_TRANSACTED)) + if ((_acknowledgeMode == CLIENT_ACKNOWLEDGE) || (_acknowledgeMode == SESSION_TRANSACTED)|| + ((_acknowledgeMode == AUTO_ACKNOWLEDGE || _acknowledgeMode == DUPS_OK_ACKNOWLEDGE ) && hasMessageListeners())) { if (_logger.isDebugEnabled()) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 7bb400fada..c6e5fbb019 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -147,7 +147,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa private List<StackTraceElement> _closedStack = null; - protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination, String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, AMQProtocolHandler protocolHandler, @@ -211,6 +210,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa ft.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector == null ? "" : messageSelector); _arguments = ft; + } public AMQDestination getDestination() @@ -814,31 +814,6 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } } - - /** - * Acknowledge up to last message delivered (if any). Used when commiting. - * - * @return the lastDeliveryTag to acknowledge - */ - Long getLastDelivered() - { - if (!_receivedDeliveryTags.isEmpty()) - { - Long lastDeliveryTag = _receivedDeliveryTags.poll(); - - while (!_receivedDeliveryTags.isEmpty()) - { - lastDeliveryTag = _receivedDeliveryTags.poll(); - } - - assert _receivedDeliveryTags.isEmpty(); - - return lastDeliveryTag; - } - - return null; - } - void notifyError(Throwable cause) { // synchronized (_closed) diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java index 47c20b683c..bb277887aa 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java @@ -470,7 +470,8 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM } } - _0_10session.getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + _0_10session.flushProcessed(ranges, false); + _0_10session.getQpidSession().messageRelease(ranges); clearReceiveQueue(); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java index cf1d7cedeb..efcbfd5532 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java @@ -28,7 +28,10 @@ import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.message.*; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.framing.*; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.BindingURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +39,8 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe { protected final Logger _logger = LoggerFactory.getLogger(getClass()); + private final RejectBehaviour _rejectBehaviour; + protected BasicMessageConsumer_0_8(int channelId, AMQConnection connection, AMQDestination destination, String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, AMQProtocolHandler protocolHandler, FieldTable rawSelector, int prefetchHigh, int prefetchLow, @@ -55,6 +60,25 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe consumerArguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE); } + if (destination.getRejectBehaviour() != null) + { + _rejectBehaviour = destination.getRejectBehaviour(); + } + else + { + ConnectionURL connectionURL = connection.getConnectionURL(); + String rejectBehaviour = connectionURL.getOption(ConnectionURL.OPTIONS_REJECT_BEHAVIOUR); + if (rejectBehaviour != null) + { + _rejectBehaviour = RejectBehaviour.valueOf(rejectBehaviour.toUpperCase()); + } + else + { + // use the default value for all connections, if not set + rejectBehaviour = System.getProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, RejectBehaviour.NORMAL.toString()); + _rejectBehaviour = RejectBehaviour.valueOf( rejectBehaviour.toUpperCase()); + } + } } void sendCancel() throws AMQException, FailoverException @@ -89,4 +113,9 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe { } + + public RejectBehaviour getRejectBehaviour() + { + return _rejectBehaviour; + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/RejectBehaviour.java b/qpid/java/client/src/main/java/org/apache/qpid/client/RejectBehaviour.java new file mode 100644 index 0000000000..e3c958044e --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/RejectBehaviour.java @@ -0,0 +1,32 @@ +/* + * + * 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.qpid.client; + +/** + * This enum can be used only with for 0-8/0-9/0-9-1 protocols connections to notify + * the client to delegate the requeue/DLQ decision to the server + * if <code>SERVER</server> value is specified. Otherwise the messages won't be moved to + * the DLQ (or dropped) when delivery count exceeds the maximum. + */ +public enum RejectBehaviour +{ + NORMAL, SERVER; +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java index 81b9940ed5..0f3be4ba18 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java @@ -123,7 +123,7 @@ public class TopicPublisherAdapter implements TopicPublisher public void send(Destination dest, Message msg) throws JMSException { checkPreConditions(); - checkTopic(_topic); + checkTopic(dest); _delegate.send(dest, msg); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java index 97048f39f4..509aa25bd5 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java @@ -5,9 +5,9 @@ * 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 @@ -75,4 +75,11 @@ public class XAConnectionImpl extends AMQConnection implements XAConnection, XAQ { return (XATopicSession) createXASession(); } + + //Specialized call for JCA + public XASession createXASession(int ackMode) throws JMSException + { + checkNotClosed(); + return _delegate.createXASession(ackMode); + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java index 6b9121811d..aaabf613fc 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java @@ -5,9 +5,9 @@ * 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 - * + * + * 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 @@ -43,21 +43,36 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic private Session _jmsSession; - //-- Constructors + // Constructors /** * Create a JMS XASession */ public XASessionImpl(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, int defaultPrefetchHigh, int defaultPrefetchLow) { - super(qpidConnection, con, channelId, false, // this is not a transacted session - Session.AUTO_ACKNOWLEDGE, // the ack mode is transacted - MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow,null); + this(qpidConnection, con, channelId, false, Session.AUTO_ACKNOWLEDGE, + MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow, null); + } + + public XASessionImpl(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, + int ackMode, int defaultPrefetchHigh, int defaultPrefetchLow) + { + this(qpidConnection, con, channelId, false, ackMode, MessageFactoryRegistry.newDefaultRegistry(), + defaultPrefetchHigh, defaultPrefetchLow, null); + + } + + public XASessionImpl(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, + boolean transacted, int ackMode, MessageFactoryRegistry registry, int defaultPrefetchHigh, int defaultPrefetchLow, + String name) + { + super(qpidConnection, con, channelId, transacted, ackMode, registry, defaultPrefetchHigh, defaultPrefetchLow, name); createSession(); _xaResource = new XAResourceImpl(this); - } + } + - //-- public methods + // public methods /** * Create a qpid session. @@ -70,7 +85,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic } - //--- javax.njms.XASEssion API + // javax.njms.XASEssion API /** * Gets the session associated with this XASession. @@ -97,7 +112,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic return _xaResource; } - //-- overwritten mehtods + // overwritten mehtods /** * Throws a {@link TransactionInProgressException}, since it should * not be called for an XASession object. @@ -132,7 +147,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic return _qpidDtxSession; } - //--- interface XAQueueSession + // interface XAQueueSession /** * Gets the topic session associated with this <CODE>XATopicSession</CODE>. * @@ -144,7 +159,7 @@ public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopic return (QueueSession) getSession(); } - //--- interface XATopicSession + // interface XATopicSession /** * Gets the topic session associated with this <CODE>XATopicSession</CODE>. diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties index b04a756e80..8855a040ea 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties @@ -6,9 +6,9 @@ # 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 @@ -30,3 +30,4 @@ CRAM-MD5-HASHED.3=org.apache.qpid.client.security.UsernameHashedPasswordCallback CRAM-MD5.4=org.apache.qpid.client.security.UsernamePasswordCallbackHandler AMQPLAIN.5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler PLAIN.6=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +ANONYMOUS.7=org.apache.qpid.client.security.UsernamePasswordCallbackHandler diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java index 26641982d7..24d9360cfa 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java @@ -41,7 +41,16 @@ public interface ConnectionURL public static final String OPTIONS_USE_LEGACY_MAP_MESSAGE_FORMAT = "use_legacy_map_msg_format"; public static final String OPTIONS_BROKERLIST = "brokerlist"; public static final String OPTIONS_FAILOVER = "failover"; - public static final String OPTIONS_FAILOVER_CYCLE = "cyclecount"; + public static final String OPTIONS_FAILOVER_CYCLE = "cyclecount"; + + /** + * This option is only applicable for 0-8/0-9/0-9-1 protocols connection + * <p> + * It tells the client to delegate the requeue/DLQ decision to the + * server .If this option is not specified, the messages won't be moved to + * the DLQ (or dropped) when delivery count exceeds the maximum. + */ + public static final String OPTIONS_REJECT_BEHAVIOUR = "rejectbehaviour"; public static final String OPTIONS_DEFAULT_TOPIC_EXCHANGE = "defaultTopicExchange"; public static final String OPTIONS_DEFAULT_QUEUE_EXCHANGE = "defaultQueueExchange"; public static final String OPTIONS_TEMPORARY_TOPIC_EXCHANGE = "temporaryTopicExchange"; diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/AMQConnectionUnitTest.java b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQConnectionUnitTest.java new file mode 100644 index 0000000000..3a565f0f0d --- /dev/null +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQConnectionUnitTest.java @@ -0,0 +1,66 @@ +/* + * + * 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.qpid.client; + +import java.util.concurrent.atomic.AtomicReference; + +import javax.jms.ExceptionListener; +import javax.jms.JMSException; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQInvalidArgumentException; + +public class AMQConnectionUnitTest extends TestCase +{ + + public void testExceptionReceived() + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'"; + AMQInvalidArgumentException expectedException = new AMQInvalidArgumentException("Test", null); + final AtomicReference<JMSException> receivedException = new AtomicReference<JMSException>(); + try + { + MockAMQConnection connection = new MockAMQConnection(url); + connection.setExceptionListener(new ExceptionListener() + { + + @Override + public void onException(JMSException jmsException) + { + receivedException.set(jmsException); + } + }); + connection.exceptionReceived(expectedException); + } + catch (Exception e) + { + fail("Failure to test exceptionRecived:" + e.getMessage()); + } + JMSException exception = receivedException.get(); + assertNotNull("Expected JMSException but got null", exception); + assertEquals("JMSException error code is incorrect", Integer.toString(expectedException.getErrorCode().getCode()), exception.getErrorCode()); + assertNotNull("Expected not null message for JMSException", exception.getMessage()); + assertTrue("JMSException error message is incorrect", exception.getMessage().contains(expectedException.getMessage())); + assertEquals("JMSException linked exception is incorrect", expectedException, exception.getLinkedException()); + } + +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java index 68531eee84..f53fa8d83c 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/AMQSession_0_10Test.java @@ -287,29 +287,6 @@ public class AMQSession_0_10Test extends TestCase assertNotNull("ExecutionSync was not sent", event); } - public void testRejectMessage() - { - AMQSession_0_10 session = createAMQSession_0_10(); - session.rejectMessage(1l, true); - ProtocolEvent event = findSentProtocolEventOfClass(session, MessageRelease.class, false); - assertNotNull("MessageRelease event was not sent", event); - } - - public void testReleaseForRollback() - { - AMQSession_0_10 session = createAMQSession_0_10(); - try - { - session.releaseForRollback(); - } - catch (Exception e) - { - fail("Unexpected exception is cought:" + e.getMessage()); - } - ProtocolEvent event = findSentProtocolEventOfClass(session, MessageRelease.class, false); - assertNotNull("MessageRelease event was not sent", event); - } - public void testSendQueueDelete() { AMQSession_0_10 session = createAMQSession_0_10(); diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/BasicMessageConsumer_0_8_Test.java b/qpid/java/client/src/test/java/org/apache/qpid/client/BasicMessageConsumer_0_8_Test.java new file mode 100644 index 0000000000..d8d94ba40e --- /dev/null +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/BasicMessageConsumer_0_8_Test.java @@ -0,0 +1,104 @@ +/* + * + * 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.qpid.client; + +import javax.jms.Session; + +import org.apache.qpid.test.unit.message.TestAMQSession; +import org.apache.qpid.url.AMQBindingURL; + +import junit.framework.TestCase; + +public class BasicMessageConsumer_0_8_Test extends TestCase +{ + /** + * Test that if there is a value for Reject Behaviour specified for the Destination + * used to create the Consumer, it overrides the value for the Connection. + */ + public void testDestinationRejectBehaviourOverridesDefaultConnection() throws Exception + { + /* + * Check that when the connection does not have a value applied that this + * is successfully overridden with a specific value by the consumer. + */ + String connUrlString = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'"; + AMQConnection conn = new MockAMQConnection(connUrlString); + + String url = "exchangeClass://exchangeName/Destination/Queue?rejectbehaviour='server'"; + AMQBindingURL burl = new AMQBindingURL(url); + AMQDestination queue = new AMQQueue(burl); + + AMQSession<BasicMessageConsumer_0_8, BasicMessageProducer_0_8> testSession = new TestAMQSession(conn); + BasicMessageConsumer_0_8 consumer = new BasicMessageConsumer_0_8(0, conn, queue, "", false, null, testSession, null, null, 10, 5, false, Session.SESSION_TRANSACTED, false, false); + + assertEquals("Reject behaviour was was not as expected", RejectBehaviour.SERVER, consumer.getRejectBehaviour()); + } + + /** + * Check that when the connection does have a specific value applied that this + * is successfully overridden with another specific value by the consumer. + */ + public void testDestinationRejectBehaviourSpecified() throws Exception + { + final String connUrlString = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'&rejectbehaviour='server'"; + final AMQConnection conn = new MockAMQConnection(connUrlString); + + final String url = "exchangeClass://exchangeName/Destination/Queue?rejectbehaviour='normal'"; + final AMQBindingURL burl = new AMQBindingURL(url); + final AMQDestination queue = new AMQQueue(burl); + + final AMQSession<BasicMessageConsumer_0_8, BasicMessageProducer_0_8> testSession = new TestAMQSession(conn); + final BasicMessageConsumer_0_8 consumer = new BasicMessageConsumer_0_8(0, conn, queue, "", false, null, testSession, null, null, 10, 5, false, Session.SESSION_TRANSACTED, false, false); + + assertEquals("Reject behaviour was was not as expected", RejectBehaviour.NORMAL, consumer.getRejectBehaviour()); + } + + /** + * Test that if no value for Reject Behaviour is applied to the Destination, then the value + * from the connection is used and acts as expected. + */ + public void testRejectBehaviourDetectedFromConnection() throws Exception + { + /* + * Check that when the connection does have a specific value applied that this + * is successfully detected by the consumer. + */ + String connUrlString = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'&rejectbehaviour='normal'"; + AMQConnection conn = new MockAMQConnection(connUrlString); + + String url = "exchangeClass://exchangeName/Destination/Queue"; + AMQBindingURL burl = new AMQBindingURL(url); + AMQDestination queue = new AMQQueue(burl); + + assertNull("Reject behaviour should have been null", queue.getRejectBehaviour()); + + AMQSession<BasicMessageConsumer_0_8, BasicMessageProducer_0_8> testSession = new TestAMQSession(conn); + BasicMessageConsumer_0_8 consumer = new BasicMessageConsumer_0_8(0, conn, queue, "", false, null, testSession, null, null, 10, 5, false, Session.SESSION_TRANSACTED, false, false); + + assertEquals("Reject behaviour was was not as expected", RejectBehaviour.NORMAL, consumer.getRejectBehaviour()); + } + + + protected RejectBehaviour getRejectBehaviour(AMQDestination destination) + { + return destination.getRejectBehaviour(); + } +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java index 73e67469ae..919809edc3 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java @@ -55,4 +55,9 @@ public class MockAMQConnection extends AMQConnection _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_OPEN); return null; } + + public AMQConnectionDelegate getDelegate() + { + return _delegate; + } } diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java index 9095f94960..506185cbaf 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java @@ -20,19 +20,35 @@ */ package org.apache.qpid.test.unit.client.BrokerDetails; -import java.util.HashMap; -import java.util.Map; - import junit.framework.TestCase; import org.apache.qpid.client.AMQBrokerDetails; -import org.apache.qpid.client.AMQConnectionURL; -import org.apache.qpid.jms.ConnectionURL; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.url.URLSyntaxException; public class BrokerDetailsTest extends TestCase { + public void testDefaultTCP_NODELAY() throws URLSyntaxException + { + String brokerURL = "tcp://localhost:5672"; + AMQBrokerDetails broker = new AMQBrokerDetails(brokerURL); + + assertNull("default value should be null", broker.getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)); + } + + public void testOverridingTCP_NODELAY() throws URLSyntaxException + { + String brokerURL = "tcp://localhost:5672?tcp_nodelay='true'"; + AMQBrokerDetails broker = new AMQBrokerDetails(brokerURL); + + assertTrue("value should be true", Boolean.valueOf(broker.getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY))); + + brokerURL = "tcp://localhost:5672?tcp_nodelay='false''&maxprefetch='1'"; + broker = new AMQBrokerDetails(brokerURL); + + assertFalse("value should be false", Boolean.valueOf(broker.getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY))); + } + public void testMultiParameters() throws URLSyntaxException { String url = "tcp://localhost:5672?timeout='200',immediatedelivery='true'"; @@ -82,9 +98,4 @@ public class BrokerDetailsTest extends TestCase } } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(BrokerDetailsTest.class); - } } diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java index 4624b36fea..392ef1f29b 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java @@ -7,9 +7,9 @@ * 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 @@ -38,7 +38,7 @@ public class ConnectionURLTest extends TestCase ConnectionURL connectionurl = new AMQConnectionURL(url); assertTrue(connectionurl.getFailoverMethod().equals("roundrobin")); - assertEquals("100", connectionurl.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE)); + assertEquals("100", connectionurl.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE)); assertTrue(connectionurl.getUsername().equals("ritchiem")); assertTrue(connectionurl.getPassword().equals("bob")); assertTrue(connectionurl.getVirtualHost().equals("/test")); @@ -274,6 +274,34 @@ public class ConnectionURLTest extends TestCase // assertTrue(service.getPort() == 1234); } + /** + * Test for QPID-3662 to ensure the {@code toString()} representation is correct. + */ + public void testConnectionURLOptionToString() throws URLSyntaxException + { + String url = "amqp://guest:guest@client/localhost?maxprefetch='1'&brokerlist='tcp://localhost:1234?tcp_nodelay='true''"; + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertNull(connectionurl.getFailoverMethod()); + assertEquals("guest", connectionurl.getUsername()); + assertEquals("guest", connectionurl.getPassword()); + assertEquals("client", connectionurl.getClientName()); + assertEquals("/localhost", connectionurl.getVirtualHost()); + assertEquals("1", connectionurl.getOption("maxprefetch")); + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 1234); + assertTrue(service.getProperties().containsKey("tcp_nodelay")); + assertEquals("true", service.getProperties().get("tcp_nodelay")); + + String nopasswd = "amqp://guest:********@client/localhost?maxprefetch='1'&brokerlist='tcp://localhost:1234?tcp_nodelay='true''"; + String tostring = connectionurl.toString(); + assertEquals(tostring.indexOf("maxprefetch"), tostring.lastIndexOf("maxprefetch")); + assertEquals(nopasswd, tostring); + } public void testSingleTransportMultiOptionURL() throws URLSyntaxException { @@ -338,7 +366,7 @@ public class ConnectionURLTest extends TestCase assertTrue(connectionurl.getPassword().equals("pass")); assertTrue(connectionurl.getVirtualHost().equals("/test")); assertTrue(connectionurl.getClientName().equals("client_id")); - + assertTrue(connectionurl.getBrokerCount() == 1); } @@ -457,7 +485,6 @@ public class ConnectionURLTest extends TestCase assertTrue(service.getTransport().equals("tcp")); - assertTrue(service.getHost().equals("localhost")); assertTrue(service.getPort() == 5672); assertEquals("jim",service.getProperty("foo")); @@ -468,7 +495,7 @@ public class ConnectionURLTest extends TestCase assertTrue(connectionurl.getOption("timeout").equals("200")); assertTrue(connectionurl.getOption("immediatedelivery").equals("true")); } - + /** * Test that options other than failover and brokerlist are returned in the string representation. * <p> @@ -477,7 +504,7 @@ public class ConnectionURLTest extends TestCase public void testOptionToString() throws Exception { ConnectionURL url = new AMQConnectionURL("amqp://user:pass@temp/test?maxprefetch='12345'&brokerlist='tcp://localhost:5672'"); - + assertTrue("String representation should contain options and values", url.toString().contains("maxprefetch='12345'")); } @@ -493,10 +520,10 @@ public class ConnectionURLTest extends TestCase assertTrue(connectionurl.getBrokerCount() == 1); BrokerDetails service = connectionurl.getBrokerDetails(0); - assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getTransport().equals("tcp")); assertTrue(service.getHost().equals("under_score")); assertTrue(service.getPort() == 6672); - + url = "amqp://guest:guest@clientid/test?brokerlist='tcp://under_score'"; connectionurl = new AMQConnectionURL(url); @@ -507,11 +534,44 @@ public class ConnectionURLTest extends TestCase assertTrue(connectionurl.getBrokerCount() == 1); service = connectionurl.getBrokerDetails(0); - assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getTransport().equals("tcp")); assertTrue(service.getHost().equals("under_score")); assertTrue(service.getPort() == 5672); } - + + + public void testRejectBehaviourPresent() throws Exception + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'&rejectbehaviour='server'"; + + ConnectionURL connectionURL = new AMQConnectionURL(url); + + assertTrue(connectionURL.getFailoverMethod() == null); + assertTrue(connectionURL.getUsername().equals("guest")); + assertTrue(connectionURL.getPassword().equals("guest")); + assertTrue(connectionURL.getVirtualHost().equals("/test")); + + //check that the reject behaviour option is returned as expected + assertEquals("Reject behaviour option was not as expected", "server", + connectionURL.getOption(ConnectionURL.OPTIONS_REJECT_BEHAVIOUR)); + } + + public void testRejectBehaviourNotPresent() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'&foo='bar'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + //check that the reject behaviour option is null as expected + assertNull("Reject behaviour option was not as expected", + connectionurl.getOption(ConnectionURL.OPTIONS_REJECT_BEHAVIOUR)); + } + public static junit.framework.Test suite() { return new junit.framework.TestSuite(ConnectionURLTest.class); diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java index 7de09cff45..2c32e4c559 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java @@ -22,8 +22,11 @@ package org.apache.qpid.test.unit.client.destinationurl; import junit.framework.TestCase; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.RejectBehaviour; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -190,6 +193,67 @@ public class DestinationURLTest extends TestCase assertTrue(dest.getQueueName().equals("test:testQueueD")); } + public void testRejectBehaviourPresent() throws URISyntaxException + { + String url = "exchangeClass://exchangeName/Destination/Queue?rejectbehaviour='server'"; + + AMQBindingURL burl = new AMQBindingURL(url); + + assertTrue(url.equals(burl.toString())); + assertTrue(burl.getExchangeClass().equals("exchangeClass")); + assertTrue(burl.getExchangeName().equals("exchangeName")); + assertTrue(burl.getDestinationName().equals("Destination")); + assertTrue(burl.getQueueName().equals("Queue")); + + //check that the MaxDeliveryCount property has the right value + assertEquals("server",burl.getOption(BindingURL.OPTION_REJECT_BEHAVIOUR)); + + //check that the MaxDeliveryCount value is correctly returned from an AMQDestination + class MyTestAMQDestination extends AMQDestination + { + public MyTestAMQDestination(BindingURL url) + { + super(url); + } + public boolean isNameRequired() + { + return false; + } + }; + + AMQDestination dest = new MyTestAMQDestination(burl); + assertEquals("Reject behaviour is unexpected", RejectBehaviour.SERVER, dest.getRejectBehaviour()); + } + + public void testRejectBehaviourNotPresent() throws URISyntaxException + { + String url = "exchangeClass://exchangeName/Destination/Queue"; + + AMQBindingURL burl = new AMQBindingURL(url); + + assertTrue(url.equals(burl.toString())); + + assertTrue(burl.getExchangeClass().equals("exchangeClass")); + assertTrue(burl.getExchangeName().equals("exchangeName")); + assertTrue(burl.getDestinationName().equals("Destination")); + assertTrue(burl.getQueueName().equals("Queue")); + + class MyTestAMQDestination extends AMQDestination + { + public MyTestAMQDestination(BindingURL url) + { + super(url); + } + public boolean isNameRequired() + { + return false; + } + }; + + AMQDestination dest = new MyTestAMQDestination(burl); + assertNull("Reject behaviour is unexpected", dest.getRejectBehaviour()); + } + public static junit.framework.Test suite() { return new junit.framework.TestSuite(DestinationURLTest.class); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java b/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java index 62ded5b2d8..a36e7c214e 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/configuration/ClientProperties.java @@ -102,6 +102,20 @@ public class ClientProperties */ public static final int DEFAULT_SYNC_OPERATION_TIMEOUT = 60000; + /** + * System properties to change the default value used for TCP_NODELAY + */ + public static final String QPID_TCP_NODELAY_PROP_NAME = "qpid.tcp_nodelay"; + public static final String AMQJ_TCP_NODELAY_PROP_NAME = "amqj.tcp_nodelay"; + + /** + * System property to set the reject behaviour. default value will be 'normal' but can be + * changed to 'server' in which case the server decides whether a message should be requeued + * or dead lettered. + * This can be overridden by the more specific settings at connection or binding URL level. + */ + public static final String REJECT_BEHAVIOUR_PROP_NAME = "qpid.reject.behaviour"; + /* public static final QpidProperty<Boolean> IGNORE_SET_CLIENTID_PROP_NAME = QpidProperty.booleanProperty(false,"qpid.ignore_set_client_id","ignore_setclientID"); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java index 37a8e594c0..2ee507e2ec 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java @@ -22,6 +22,8 @@ package org.apache.qpid.transport; import java.util.Map; +import org.apache.qpid.configuration.ClientProperties; + /** * A ConnectionSettings object can only be associated with * one Connection object. I have added an assertion that will @@ -38,7 +40,8 @@ public class ConnectionSettings String username = "guest"; String password = "guest"; int port = 5672; - boolean tcpNodelay = Boolean.getBoolean("amqj.tcp_nodelay"); + boolean tcpNodelay = Boolean.valueOf(System.getProperty(ClientProperties.QPID_TCP_NODELAY_PROP_NAME, + System.getProperty(ClientProperties.AMQJ_TCP_NODELAY_PROP_NAME, "true"))); int maxChannelCount = 32767; int maxFrameSize = 65535; int heartbeatInterval; diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java index e1d1596ec5..838a662402 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java @@ -60,7 +60,8 @@ public class IoNetworkTransport implements OutgoingNetworkTransport, IncomingNet LOGGER.debug("SO_RCVBUF : %s", _socket.getReceiveBufferSize()); LOGGER.debug("SO_SNDBUF : %s", _socket.getSendBufferSize()); - + LOGGER.debug("TCP_NODELAY : %s", _socket.getTcpNoDelay()); + InetAddress address = InetAddress.getByName(settings.getHost()); _socket.connect(new InetSocketAddress(address, settings.getPort())); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java index d4b5975e54..5b714434d9 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java @@ -33,6 +33,8 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; +import javax.net.ssl.SSLSocket; + /** * IoReceiver * @@ -94,7 +96,7 @@ final class IoReceiver implements Runnable, Closeable { try { - if (shutdownBroken) + if (shutdownBroken || socket instanceof SSLSocket) { socket.close(); } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java index 17f89c34ef..08934004a8 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayerFactory.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.transport.network.security; import org.apache.qpid.ssl.SSLContextFactory; diff --git a/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java b/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java index 9996fff311..0e6c865a16 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java @@ -37,6 +37,15 @@ public interface BindingURL public static final String OPTION_ROUTING_KEY = "routingkey"; public static final String OPTION_BINDING_KEY = "bindingkey"; + /** + * This option is only applicable for 0-8/0-9/0-9-1 protocols connection + * <p> + * It tells the client to delegate the requeue/DLQ decision to the + * server .If this option is not specified, the messages won't be moved to + * the DLQ (or dropped) when delivery count exceeds the maximum. + */ + public static final String OPTION_REJECT_BEHAVIOUR = "rejectbehaviour"; + String getURL(); diff --git a/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionSettingsTest.java b/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionSettingsTest.java new file mode 100644 index 0000000000..57c0549193 --- /dev/null +++ b/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionSettingsTest.java @@ -0,0 +1,69 @@ +/* + * + * 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.qpid.transport; + +import org.apache.qpid.configuration.ClientProperties; +import org.apache.qpid.test.utils.QpidTestCase; + +public class ConnectionSettingsTest extends QpidTestCase +{ + ConnectionSettings _conConnectionSettings; + + protected void setUp() throws Exception + { + super.setUp(); + _conConnectionSettings = new ConnectionSettings(); + } + + public void testDefaultTCP_NODELAY() + { + assertTrue("Default for isTcpNodelay() should be true", _conConnectionSettings.isTcpNodelay()); + } + + public void testSystemPropertyOverrideTrueForTCP_NODELAY() + { + systemPropertyOverrideForTCP_NODELAYImpl(ClientProperties.QPID_TCP_NODELAY_PROP_NAME, true); + } + + public void testSystemPropertyOverrideFalseForTCP_NODELAY() + { + systemPropertyOverrideForTCP_NODELAYImpl(ClientProperties.QPID_TCP_NODELAY_PROP_NAME, false); + } + + public void testLegacySystemPropertyOverrideTrueForTCP_NODELAY() + { + systemPropertyOverrideForTCP_NODELAYImpl(ClientProperties.AMQJ_TCP_NODELAY_PROP_NAME, true); + } + + public void testLegacySystemPropertyOverrideFalseForTCP_NODELAY() + { + systemPropertyOverrideForTCP_NODELAYImpl(ClientProperties.AMQJ_TCP_NODELAY_PROP_NAME, false); + } + + private void systemPropertyOverrideForTCP_NODELAYImpl(String propertyName, boolean value) + { + //set the default via system property + setTestSystemProperty(propertyName, String.valueOf(value)); + + _conConnectionSettings = new ConnectionSettings(); + assertEquals("Value for isTcpNodelay() is incorrect", value, _conConnectionSettings.isTcpNodelay()); + } +} diff --git a/qpid/java/common/src/test/java/org/apache/qpid/util/default.properties b/qpid/java/common/src/test/java/org/apache/qpid/util/default.properties index cb522ea9a7..8214cc1fee 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/util/default.properties +++ b/qpid/java/common/src/test/java/org/apache/qpid/util/default.properties @@ -1,2 +1,21 @@ +# +# 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. +# + # Used by FileUtilsTests src=default.properties
\ No newline at end of file diff --git a/qpid/java/common/src/test/java/org/apache/qpid/util/mydefaults.properties b/qpid/java/common/src/test/java/org/apache/qpid/util/mydefaults.properties index 6a49d927d0..c0a21d08d1 100644 --- a/qpid/java/common/src/test/java/org/apache/qpid/util/mydefaults.properties +++ b/qpid/java/common/src/test/java/org/apache/qpid/util/mydefaults.properties @@ -1,2 +1,21 @@ +# +# 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. +# + # Used by FileUtilsTests src=mydefaults
\ No newline at end of file diff --git a/qpid/java/jca/README-GERONIMO.txt b/qpid/java/jca/README-GERONIMO.txt new file mode 100644 index 0000000000..3b72a7e094 --- /dev/null +++ b/qpid/java/jca/README-GERONIMO.txt @@ -0,0 +1,29 @@ +Qpid JCA Resource Adapter + +Apache Geronimo 2.x Installation and Configuration Instructions + +Overview +======== +The Qpid Resource Adapter is a JCA 1.5 compliant resource adapter that allows +for JEE integration between EE applications and AMQP 0.10 message brokers. + +The adapter provides both outbound and inbound connectivity and +exposes a variety of options to fine tune your messaging applications. +Currently the adapter only supports C++ based brokers and has only been tested with Apache Qpid C++ broker. + + +The following document explains how to configure the resource adapter for deployment in Geronimo 2.x + +Configuration and Deployment +============================ + +The Apache Geronimo 2.x application server requires the use of an RA deployment plan to deploy and configure +a resource adapter. A sample deployment plan has been provided as geronimo-ra.xml which is included in the +META-INF directory of the qpid-ra-<version>.rar file. If you need to modify this file, simply extract +the RAR file, edit the geronimo-ra.xml file and recompress the file. + +Please refer to the general README.txt file for a description of each configuration property +the adapter supports for resource adapter, managedconnectionfatory and activationspec level configuration. + + + diff --git a/qpid/java/jca/README-JBOSS.txt b/qpid/java/jca/README-JBOSS.txt new file mode 100644 index 0000000000..77bf91e6dd --- /dev/null +++ b/qpid/java/jca/README-JBOSS.txt @@ -0,0 +1,168 @@ +Qpid JCA Resource Adapter + +JBoss EAP 5.x Installation and Configuration Instructions + +Overview +======== +The Qpid Resource Adapter is a JCA 1.5 compliant resource adapter that allows +for JEE integration between EE applications and AMQP 0.10 message brokers. + +The adapter provides both outbound and inbound connectivity and +exposes a variety of options to fine tune your messaging applications. +Currently the adapter only supports C++ based brokers and has only been tested with Apache Qpid C++ broker. + +The following document explains how to configure the resource adapter for deployment in JBoss EAP 5.x. + + +Deployment +========== +To deploy the Qpid JCA adapter for either JBoss EAP, simply copy the qpid-ra-<version>.rar file +to your JBoss deploy directory. By default this can be found at JBOSS_ROOT/server/<server-name>/deploy, +where JBOSS_ROOT denotes the root directory of your JBoss installation and <server-name> denotes the +name of your deployment server. A successful adapter installation will be accompanied by the +following INFO message: + + INFO [QpidResourceAdapter] Qpid resource adaptor started + +At this point the adapter is deployed and ready for configuration. + +Configuration Overview +====================== +The standard configuration mechanism for 1.5 JCA adapters is the ra.xml +deployment descriptor. Like other EE based descriptors this file can be found +in the META-INF directory of the provided EE artifact (ie .rar file). A majority +of the properties in the ra.xml will seem familiar to anyone who has worked with +Apache Qpid in a standalone environment. A reasonable set of configuration defaults +have been provided. + +The resource adapter configuration properties provide generic properties for both +inbound and outbound connectivity. These properties can be overridden when deploying +managed connection factories as well as inbound activations using the standard JBoss +configuration artifacts, the *-ds.xml file and MDB activation spec . A sample *-ds.xml file, +qpid-jca-ds.xml, can be found in your Qpid JCA resource adapter directory. + +The general README.txt file provides a detailed description of all the properties associated +with the Qpid JCA Resource adapter. Please consult this file for further explanation of +how configuration properties are treated within the Qpid JCA adapter. + +ConnectionFactory Configuration +====================================== +As per the JCA specification, the standard outbound-connectivity component is the +ConnectionFactory. In EAP 5.x ConnectionFactories are configured +via the *-ds.xml file. As previously mentioned, a sample *-ds.xml file, qpid-jca-ds.xml +hasbeen provided with your distribution. This file can be easily modified to suit +your development/deployment needs. The following describes the ConnectionFactory +portion of the sample file. + +XA ConnectionFactory +==================== + <tx-connection-factory> + <jndi-name>QpidJMSXA</jndi-name> + <xa-transaction/> + <rar-name>qpid-ra-<ra-version>.rar</rar-name> + <connection-definition>org.apache.qpid.ra.QpidRAConnectionFactory</connection-definition> + <config-property name="connectionURL">amqp://guest:guest@/test?brokerlist='tcp://localhost:5672?sasl_mechs='ANONYMOUS''</config-property> + <max-pool-size>20</max-pool-size> + </tx-connection-factory> + +The QpidJMSXA connection factory defines an XA capable ManagedConnectionFactory. You will need to insert your particular rar version for +the rar-name property. The jndi-name and connectionURL property are both configurable and can be modified for your environment. After deployment +the ConnectionFactory will be bound into JNDI under the name + +java:<jndi-name> + +For the previous example, this would resolve to + +java:QpidJMSXA + +Local ConnectionFactory +======================= + <tx-connection-factory> + <jndi-name>QpidJMS</jndi-name> + <rar-name>qpid-ra-0.10.rar</rar-name> + <local-transaction/> + <config-property name="useLocalTx" type="java.lang.Boolean">true</config-property> + <config-property name="connectionURL">amqp://anonymous:@client/test?brokerlist='tcp://localhost:5672?sasl_mechs='ANONYMOUS''</config-property> + <connection-definition>org.apache.qpid.ra.QpidRAConnectionFactory</connection-definition> + <max-pool-size>20</max-pool-size> + </tx-connection-factory> + +The QpidJMS connection factory defines a non XA connection factory. Typically this is used as a specialized ConnectionFactory where either XA +is not desired, or you are running with a clustered Qpid Broker configuration which at this time, does not support XA. The configuration +properties mirror those of the XA ConnectionFactory. + +Admininstered Object Configuration +================================== +Destinations (queues, topics) are configured in EAP via JCA standard Administered Objects (AdminObjects). These objects +are placed within the *-ds.xml file alongside your ConnectionFactory configurations. The sample file qpid-jca-ds.xml +provides two such objects + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=HelloQueue"> + <attribute name="JNDIName">Hello</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='qpid-ra-0.10.rar'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=QUEUE + destinationAddress=amq.direct + </attribute> + </mbean> + +The above XML defines a JMS Queue which is bound into JNDI as + +queue/HelloQueue + +This destination can be retrieved from JNDI and be used for the consumption or production of messages. The desinationAddress property +can be customized for your environment. Please see the Qpid Java Client documentation for specific configuration options. + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=HelloTopic"> + <attribute name="JNDIName">HelloTopic</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='qpid-ra-0.10.rar'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=TOPIC + destinationAddress=amq.topic + </attribute> + </mbean> + + +The above XML defines a JMS Topic which is bound into JNDI as + +HelloTopic + +This destination can be retrieved from JNDI and be used for the consumption or production of messages. The desinationAddress property +can be customized for your environment. Please see the Qpid Java Client documentation for specific configuration options. + + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=QpidConnectionFactory"> + <attribute name="JNDIName">QpidConnectionFactory</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='qpid-ra-0.10.rar'</depends> + <attribute name="Type">javax.jms.ConnectionFactory</attribute> + <attribute name="Properties"> + connectionURL=amqp://anonymous:@client/test?brokerlist='tcp://localhost:5672?sasl_mechs='ANONYMOUS'' + </attribute> + </mbean> + +The above XML defines a ConnectionFactory that can be used external to EAP 5.x. Typically this connection factory +is used by standalone or 'thin' clients that do not require an application server. This object is bound into +the EAP 5.x JNDI tree as + +QpidConnectionFactory + +ActivationSpec Configuration +============================ +The standard method for inbound communication is the MessageDrivenBean architecture with is configured +via the ActivationSpec mechanism. Please see the general README.tx file for an explanation of the +QpidActivationSpec, as well as general inbound connectivity options. + +An ActivationSpec can either be configured via the Java Annotation mechanism, or in the ejb-jar.xml deployment +descriptor. + +Summary +======= +The above description for the Qpid JCA adapter for EAP 5.x is just a general guide for deploying and configuring +the Qpid JCA adapter. The sample file provided can be easily modified and it is expected you will do so to +conform to your own environment. + diff --git a/qpid/java/jca/README.txt b/qpid/java/jca/README.txt new file mode 100644 index 0000000000..29e6825c4c --- /dev/null +++ b/qpid/java/jca/README.txt @@ -0,0 +1,241 @@ +Qpid JCA Resource Adapter Installation/Configuration Instructions + +Overview +======== +The Qpid Resource Adapter is a JCA 1.5 compliant resource adapter that allows +for JEE integration between EE applications and AMQP 0.10 message brokers. + +The adapter provides both outbound and inbound connectivity and +exposes a variety of options to fine tune your messaging applications. Currently +the adapter only supports C++ based brokers and has only been tested with Apache Qpid C++ broker. + +The following document explains general configuration information for the Qpid JCA RA. Details for +specific application server platforms are provided in separate README files typically designated as +README-<server-platform>.txt. + +Configuration +============= +As per the JCA specification, there are four main components of a JCA resource adapter: + + The ResourceAdapter JavaBean + The ManagedConnectionFactory JavaBean + The ActivationSpec JavaBean + Administered Objects + +Each of these components provide configuration options in the form of properties. The Resource Adapter +JavaBean provides a set of global configuration options while the ManagedConnectionFactory JavaBean allows +for the configuration of outbound connectivity options. The ActivationSpec JavaBean provides configuration +options for inbound connectivity. + +When a ManagedConnectionFactory JavaBean or ActivationSpec JavaBean are deployed they can choose to inherit +the configuration properties from the ResourceAdapter or provide specific properties which in turn will override +the defaults. + +While some of the properties from the three componets are specific to the JCA adapter, a majority of the +properties directly correspond the the Qpid JMS client. As such, it is strongly encouraged your familiarize +yourself with the correct syntax, configuration options for the JMS client as well as the JCA adapter. Similarly, +familiarity with the 1.5 JCA specification is encouraged though not strictly required. + +The ResourceAdapter JavaBean +============================ + +The ResourceAdapter JavaBean provides global configuration options for both inbound and outbound connectivity. +The set of ResourceAdapter properties are described below. The ResourceAdapter properties can be found in the META-INF/ra.xml +deployment descriptor which is provided with the adapter. Note, deploying a ResourceAdapter, ManagedConnectionFactory +or ActivationSpec is application server specific. As such, this document provides an explanation of these properties +but not how they are configured as this is environment specific. + +ResourceAdapter JavaBean Properties +=================================== + +ClientID + The unique client identifier. From the JMS API this is used only in the context of durable subscriptions. +Default: client_id + +SetupAttempts + The number of attempts the ResourceAdapter will make to successfully setup an inbound activation on deployment, or when an exception + occurs at runtime. +Default: 5 + +SetupInterval + The interval in milliseconds the adapter will wait between setup attempts. +Default: 5000 + +UseLocalTx + Whether or not to use local transactions instead of XA. +Default: false + +DefaultUserName + The default user name to use. +Default: guest + +DefaultPassword + The default password to use. +Default: guest + +Host + The hostname/ip address of the broker. +Default: localhost + +Port + The port of the broker. +Default: 5672 + +Path + The virtual path for the connection factory. +Default: test + +ConnectionURL + The full connection URL to the broker. +Default:amqp://guest:guest@/test?brokerlist='tcp://localhost:5672' + +TransactionManagerLocatorClass + The class responsible for locating the transaction manager within a specific application server. This is a ResourceAdapter + Java Bean specific property and is application server specific. As such, it is currently commented out. Two examples have + been provided. +Default: none + +TransactionManagerLocatorMethod + The specific method on the class above used to acquire a reference to the platform specific transaction manager. + This is a ResourceAdapter Java Bean specific property and is application server specific. + As such, it is currently commented out. Two examples have been provided. +Default:none + +Note, if you require XA support, both the TransactionManagerLocatorClass and the TransactionManagerLocatorMethod +properties MUST be set. While application servers typically provide a mechanism to do this in the form of a specific +deployment descriptor, or GUI console, the ra.xml file can also be modified directly. + +The ManagedConnectionFactory JavaBean +===================================== + +The ManagedConnectionFactory JavaBean provides outbound connectivity for the Qpid JCA adapter. In addition to most of the properties +inherited from the ResourceAdapter JavaBean, the ManagedConnectionFactory JavaBean provides specific properties only applicable +to outbound connectivity. + +sessionDefaulType + The default type of Session. Currently unused. +Default: java.jms.Queue + +useTryLock + Multi-purpose property used to specify both that a lock on the underlying managed connection should be used, as well as the + wait duration to aquire the lock. Primarily used for transaction management. A null or zero value will atttempt to acquire + the lock without a duration. Anything greater than zero will wait n number of seconds before failing to acquire the lock. +Default:0 + +KeyStorePassword + The KeyStore password for SSL +Default:none + +KeyStorePath + The path to the KeyStore. +Default:none + +CertType + The type of certificate. +Default:SunX509 + +The ActivationSpec JavaBean +=========================== +The ActivationSpec JavaBean provides inbound connectivity for the Qpid JCA adapter. In addition to most of the properties +inherited from the ResourceAdapter JavaBean, the ActivationSpec JavaBean provides specific properties only applicable +to inbound connectivity. As opposed to the ResourceAdapter and ManagedConnectionFactory JavaBean a majority of the +ActivationSpec JavaBean properties have no default value. It is expected that these will be provided via Java annotations +or deployment descriptor configuration properties. The Qpid JCA adapter provides inbound connectivity in conjunction +with the Message Driven Bean architecture from the EJB 3.x specification. + +UseJNDI + Whether or not to attempt looking up an inbound destination from JNDI. If false, an attempt will be made to construct + the destination from the other ActivationSpec properties. +Default: true + +Destination + The name of the destination on which to listen for messages. +Default:none + +DestinationType + The type of destination on which to listen. Valid values are javax.jms.Queue or java.jms.Topic. +Default:none + +MessageSelector + The JMS properties that will be used in selecting specific message. Please see the JMS specification for further details. +Default:none + +AcknowlegeMode + Whether or not the client or consumer will acknowledge any messages it receives. Ignored in a transacted scenario. + Please see the JMS specification for more details. +Default:AUTO_ACKNOWLEDGE + +SubscriptionDurablity + Whether or not the subscription is durable. +Default:none + +SubscriptionName + The name of the subscription. +Default:none + +MaxSession + The maximum number of sessions that this activation supports. +Default:15 + +TransactionTimeout + The timeout for the XA transaction for inbound messages. +Default:0 + +PrefetchLow + Qpid specific -- TODO more explanation + +PrefetchHigh + Qpid specific -- TODO more explanation + + +Administered Objects +====================== +The JCA specification provides for administered objects. Ass per the specification, administered objects are +JavaBeans that specific to the messaging provider. The Qpid JCA Resource Adapter provides two administered +objects that can be used to configure JMS destinations and a specialized JMS Connection Factory respectively. +Both these administered objects have properities to support configuration and deployment. + +QpidDestinationProxy +==================== + The QpidDestinationProxy allows a developer, deployer or adminstrator to create destinations (queues or topic) and + bind these destinations into JNDI. The following lists the properties applicable to the QpidDestinationProxy + +destinationType + The type of destination to create. Valid values are QUEUE or TOPIC. + +destinationAddress + The address string of the destination. Please see the Qpid Java JMS client documentation for valid values. + +QpidConnectionFactoryProxy + The QpidConnectionFactoryProxy allows for a non-JCA ConnectionFactory to be bound into the JNDI tree. This + ConnectionFactory can in turn be used outside of the application server. Typically a ConnectionFactory of + this sort is used by Swing or other two-tier clients not requiring JCA. The QpidConnectionFactoryProxy provides + one property + +connectionURL + This is the url used to configure the connection factory. Please see the Qpid Java Client documentation for + further details. + + +Transaction Support +=================== +The Qpid JCA Resource Adapter provides three levels of transaction support: XA, LocalTransactions and NoTransaction. +Typical usage of the Qpid JCA Resource adapter implies the use of XA transactions, though there are certain scenarios +where this is not preferred. Transaction support configuration is application server specific and as such, is explained +in the corresponding documentation for each supported application server. However, there are two limitations with +the Qpid JCA adapter at this time: + +1) Currently, the Qpid C++ broker does not support he use of XA within the context of clustered brokers. As such, if +you are running in a cluster, you will need to configure the adapter to use LocalTransactions. + +2)XARecovery is currently not implemented. In the case of a system failure, in doubt transactions will have to be +manually resolved by and administrator or otherwise qualified personnel. + +Conclusion +========== +The above documentation provides a general description of the capabilities and configuration properites of the +Qpid JCA Resource Adapter. As previously mentioned, deploying an adapter in an application server requires +specific descriptors and configuration mechanisms. Please see the accompanying doc for your application server +for further details. + + diff --git a/qpid/java/jca/build.xml b/qpid/java/jca/build.xml new file mode 100644 index 0000000000..3430232003 --- /dev/null +++ b/qpid/java/jca/build.xml @@ -0,0 +1,69 @@ +<!-- + - + - 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. + - + --> +<project name="Qpid JCA RA" default="build"> + + <property name="module.depends" value="common client"/> + <property name="module.name" value="ra"/> + + <import file="../module.xml"/> + + <property name="module.rar" value="${build.lib}/${project.name}-${module.name}-${project.version}.rar"/> + <property name="module.resources" value="src/main/resources"/> + + + <target name="rar" depends="jar"> + <jar destfile="${module.rar}"> + <fileset dir="${module.resources}"> + <include name="**/*.xml"/> + </fileset> + <fileset dir="${build.lib}"> + <include name="${project.name}-ra-${project.version}.jar"/> + <include name="${project.name}-client-${project.version}.jar"/> + <include name="${project.name}-common-${project.version}.jar"/> + </fileset> + </jar> + </target> + + <!-- Create properties file for examples --> + <target name="example-properties-file"> + <copy file="example/build-properties.xml.temp" tofile="example/build-properties.xml"> + <filterset> + <filter token="project.version" value="${project.version}"/> + </filterset> + </copy> + </target> + + <!-- Copy jars for standalone examples --> + <target name="example-jars"> + <mkdir dir="example/lib"/> + <copy todir="example/lib"> + <fileset dir="${build.lib}"> + <include name="${project.name}-ra-${project.version}.jar"/> + <include name="${project.name}-client-${project.version}.jar"/> + <include name="${project.name}-common-${project.version}.jar"/> + </fileset> + </copy> + </target> + + <target name="examples" depends="example-properties-file, example-jars"/> + + <target name="build" depends="rar, examples"/> +</project> diff --git a/qpid/java/jca/example/.gitignore b/qpid/java/jca/example/.gitignore new file mode 100644 index 0000000000..597acff14b --- /dev/null +++ b/qpid/java/jca/example/.gitignore @@ -0,0 +1,2 @@ +build/* +lib/* diff --git a/qpid/java/jca/example/README.txt b/qpid/java/jca/example/README.txt new file mode 100644 index 0000000000..d94bbfa7e5 --- /dev/null +++ b/qpid/java/jca/example/README.txt @@ -0,0 +1,277 @@ +Qpid JCA Example + +Overview +======= +The Qpid JCA example provides a sample JEE application that demonstrates how to +configure, install and run applications using the Qpid JCA adapter for EE +connectivity and the Apache Qpid C++ Broker. This example code can be used as a +convenient starting point for your own development and deployment +efforts. Currently the example is supported on JBoss EAP 5.x, JBoss 6.x and +Apache Geronimo 2.x. + +Example Components +=================== +Currently the example application consists of the following components: + +Destinations and ConnectionFactories + +Any messaging application relies on destinations (queues or topics ) +in order to produce or consume messages.The Qpid JCA example provides +five destinations by default: + + HelloTopic + GoodByeTopic + HelloGoodByeTopic + HelloQueue + GoodByeQueue + QpidResponderQueue + + +Similar to destinations, ConnectionFactories are a core component of both JMS +and JCA. ConnectionFactories provide the necessary starting point to make a connection, +establish a session and produce or consume (or both) messages from your JMS provider. + +The Qpid JCA example provides three connection factories by default: + + QpidJMSXA + QpidJMS + QpidConnectionFactory + +Each of these ConnectionFactories varies in their capabilities and the context in which +they should be used. These concepts will be explained later in this document. + +The deployment configuration for destinations, and ConnectionFactories varies by platform. +In JBossEAP, the configuration mechanism is a *-ds.xml file. Geronimo 2.2.x has the notion +of a deployment plan in the form of a geronimo-ra.xml file. + +The Qpid JCA Example provides both a qpid-jca-ds.xml file as well as a geronimo-ra.xml deployment +plan. Both mechanisms provide a reasonable set of defaults to allow you to deploy the Qpid JCA +adapter in either environment and get up and running quickly. + +EJB 3.x + +There are a six EJB 3.x components provided as part of the example. + + QpidHelloSubscriberBean - MessageDrivenBean (MDB) + QpidGoodByeSubscriberBean - (MDB) + QpidHelloListenerBean - (MDB) + QpidGoodByeListenerBean - (MDB) + QpidJMSResponderBean - (MDB) + QpidTestBean - Stateless Session Bean (SLSB) + +Servlet 2.5 + + QpidTestServlet + +A sample EE 2.5 servlet is provided allowing testing from a browser versus a JNDI +client + +EE EAR archive + An EAR wrapper for the ejb and web components. + + + An RMI client used to excercise the EJB 3.x component. + +Sample *-ds.xml file + A sample *-ds.xml file is provided to create destinations and ManagedConnectionFactories + in the JBoss environment. + +Sample geronimo-ra.xml + A sample geronimo-ra.xml file is provided to create destinations and ManagedConnectionFactories + in the Geronimo environment. This file is semantically equivalent to the JBoss *-ds.xml artifact. + +A build.xml file + An ant build.xml file to configure, install and deploy the aforementioned components. + + +Requirements +============ + +Depending upon your target platform (eg. JBoss EAP or Geronimo) you will need to set either +the JBOSS_HOME or GERONIMO_HOME property. By default, these properties are assumed to be +set from your environment. This can be modified in the build.xml file. + +JBoss EAP 5.x, JBoss 6.x + To use the sample application you will need to have JBoss EAP 5.x or JBoss 6.x running. + +Geronimo 2.x + To use the sample application you will need to have Geronimo 2.x running. + +Apache Qpid Broker + To run the sample it is assumed you have an Apache Qpid C++ broker configured and running. + The example code assumes that the broker will run at localhost on port 5672. This can be + modified within the build.xml file if this does not suit your particular environment. + + +Quickstart +========== +After satifsying the above requirements you are ready to deploy and run the example application. +The steps to deploy and run in the supported application servers are largely the same, however, +if you are targeting JBoss you will either need to modify the property in the example build.xml file + + + <property name="target.platform" value="geronimo"/> + +to be jboss + + <property name="target.platform" value="jboss"/> + +or set this property via the command line. + +Example: + + ant -Dtarget.platform=jboss <target> + +**Note** +Any time you wish to change platforms, this property needs to be modified and a complete clean +and rebuild needs to be performed. + +Step 1 -- Package, Deploy and configure the Qpid JCA adapter + +The core component of the example is the Qpid JCA adapter. The following lists the steps +for the respective platforms + +**Note** + +Regardless of platform, if you are building the Qpid JCA adapter from source code +you will need to use to package the RAR file via the Ant build system. To do this, from +the example directory execute + + ant deploy-rar + +This task packages the adapter and includes the necessary dependent jar files. + + +JBoss + There are no additional steps to package the adapter for JBoss deployment. Simply copy + the qpid-ra-<qpid.version>rar to your JBoss deploy directory. + + To configure the Qpid JCA Adapter in JBoss the *-ds.xml file mechanism is used. A sample + file is provided in the conf directory. + + If the defaults are suitable, you can simply execute + + ant deploy-ds + + While any property can be modified in the qpid-jca-ds.xml file, typically you will want to + change the URL of the broker to which you are trying to connect. Rather than modifying + the qpid-jca-ds.xml file directly you can modify the + + <property name="broker.url" value="amqp://anonymous:@client/test?brokerlist='tcp://localhost:5672?sasl_mechs='ANONYMOUS''"/> + + line in the build.xml file. This will dynamically insert the broker.url value into the qpid-jca-ds.xml file. + + Once this file is copied to your JBoss deploy directory and you received no exceptions, the adapter is now deployed, configured + and ready for use. + +Geronimo + By default, the Qpid JCA adapter ships with the geronimo-ra.xml deployment plan embedded + in the RAR file. This file is located in the META-INF directory alongside the ra.xml file. + By default the adapter is configured to access a broker located at localhost with the + default port of 5672. The ANONYMOUS security mechanism is also in use. If this is not + desirable, you have two approaches to configure the adapter. + + + 1) Extract the META-INF/ra.xml file from the RAR file, modify and recompress the RAR archive + with the updated contents. + + 2) Use the example build system to package the adapter. The example build.xml file includes + a target + + package-rar + + that can be used to package the RAR file as well as allowing changes to the geronimo-ra.xml + file without having to extract the RAR file by hand. The conf/geronimo-ra.xml file is used + when you use the example build system. + + While any property can be modified in the geronimo-ra.xml file, typically you will want to + change the URL of the broker to which you are trying to connect. Rather than modifying + the geronimo-ra.xml file directly you can modify the + + + <property name="broker.url" value="amqp://anonymous:@client/test?brokerlist='tcp://10.0.1.44:5672?sasl_mechs='ANONYMOUS''"/> + + line in the build.xml file. This will dynamically insert the broker.url value into the geronimo-ra.xml file. + + Once you have made your modifications you can execute + + + ant clean package-rar deploy-rar + + Note, your Geronimo server must be running and your GERONIMO_HOME environment variable must be set. Barring any exceptions, the + adapter is now deployed and ready for use in Geronimo. + + +Step 2 -- Deploy the application component(s). + +As previously mentioned, the adapter comes with a variety of EE components for use in your respective application server. You can choose to deploy +these components individually, or as a single EAR archive. This document assumes that you will use the EAR to deploy and run the example. + +The command to package and deploy the EAR archive is the same across application servers. Executing the following command + +ant deploy-ear + +will, depending upon platform, package the EAR and deploy the archive in your respective environment. Once this step is executed, the example +is ready for use. + + +Step 3 -- Test the Example + +The Qpid JCA example provides an EJB application, as well as a Web application. Both can be used to test/run the example: + +EJB +If you want to use the EJB application to test the example you can execute + + ant run-client + +Running this command will perform a JNDI lookup of the SLSB in either JBoss or Geronimo and send a simple string to the SLSB. The SLSB will receive +this string, construct a JMS message and place this message on a configured queue. The MDB will in turn receive this message and print the contents +to the console. + +The main properties involved in this task are + +server.host +jndi.context + +These vary depending upon which application server you are runnning. These can be modified to suit your environment. Looking at the run-client task you +will see the following: + + + <sysproperty key="qpid.ejb.name" value="qpid-jcaex/QpidTestBean/remote"/> + +This is the JNDI name of the SLSB component and it varies by application server. Typically you do not have to change this. Also, the task supports another property + + + <sysproperty key="qpid.message" value="insert-value-here"/> + +You can set this property if you want to modify the message contents being routed through the system. + +Web +The Qpid JCA Example comes with a web application. To access the web component, simply use a browser of your choice and navigate to + +http://<server-host-name>:<server-port>/qpid-jca-web/qpid + +where server-host and server-port are the host and port where you are running your application server. By default this is localhost:8080. Similar to the EJB component, +the web application supports a few options: + + +http://<server-host-name>:<server-port>/qpid-jca-web/qpid?messsage=<yourmessage> + +will allow you to customize the message contents that are routed through the system. By default, the Web application posts to a configured queue in the system. If you want to +test XA functionality, or use an alternate approach, you can specify + + +http://<server-host-name>:<server-port>/qpid-jca-web/qpid?useEJB=true + +instead of posting to a queue, the web application will use the local interface of the EJB component to send the message. This is functionally equivalent to running the +RMI client. + + +Summary +======= +While conceptually simple, the Qpid JCA example provides a majority of the component types and messaging patterns you are most likely to use your development efforts. +With the Web and EJB components, you can experiment with various aspects of JCA as well as EE development in general using the Qpid Broker as your messaging provider. +While this documentation highlights the major components and steps needed to take to get the example running, the possiblities for modifcation are numerous. You are +encouraged to experiment with the example as you develop your own messaging applications. + + diff --git a/qpid/java/jca/example/build-geronimo-properties.xml b/qpid/java/jca/example/build-geronimo-properties.xml new file mode 100644 index 0000000000..79cf5adc74 --- /dev/null +++ b/qpid/java/jca/example/build-geronimo-properties.xml @@ -0,0 +1,169 @@ +<!-- + - + - 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. + - + --> +<project name="qpid-jca-example-geronimo-properties" basedir="." default=""> + + <property name="geronimo.jndi.scheme" value="name"/> + <property name="qpid.xacf.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAManagedConnectionFactory/QpidJMSXA"/> + <property name="qpid.cf.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/QpidConnectionFactory"/> + <property name="qpid.hello.queue.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/HelloQueue"/> + <property name="qpid.goodbye.queue.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/GoodByeQueue"/> + <property name="qpid.responder.queue.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/ResponderQueue"/> + <property name="qpid.hello.topic.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/HelloTopic"/> + <property name="qpid.goodbye.topic.jndi.name" value="jca:/qpid.jca/QpidJCAAdapter/JCAAdminObject/GoodByeTopic"/> + <property name="qpid.ejb.jndi.name" value="name="QpidTestEJB""/> + <property name="qpid.ejb.name" value="QpidTestBeanRemote"/> + + <property name="jndi.context" value="org.openejb.client.RemoteInitialContextFactory"/> + <property name="server.host" value="ejbd://localhost:4201"/> + <property name="geronimo.home" location="${env.GERONIMO_HOME}"/> + <property name="geronimo.user" value="system"/> + <property name="geronimo.password" value="manager"/> + + <property name="geronimo.rar.group.id" value="qpid.jca"/> + <property name="geronimo.rar.artifact.id" value="QpidJCAAdapter"/> + <property name="geronimo.rar.version" value="1.0"/> + <property name="geronimo.rar.type" value="rar"/> + <property name="geronimo.rar.id" value="${geronimo.rar.group.id}/${geronimo.rar.artifact.id}/${geronimo.rar.version}/${geronimo.rar.type}"/> + + <property name="geronimo.ejb.group.id" value="qpid.jca.example"/> + <property name="geronimo.ejb.artifact.id" value="QpidJCAEJBExample"/> + <property name="geronimo.ejb.version" value="1.0"/> + <property name="geronimo.ejb.type" value="car"/> + <property name="geronimo.ejb.id" value="${geronimo.ejb.group.id}/${geronimo.ejb.artifact.id}/${geronimo.ejb.version}/${geronimo.ejb.type}"/> + + <property name="geronimo.war.group.id" value="qpid.jca.example"/> + <property name="geronimo.war.artifact.id" value="QpidJCAWebExample"/> + <property name="geronimo.war.version" value="1.0"/> + <property name="geronimo.war.type" value="war"/> + <property name="geronimo.war.id" value="${geronimo.war.group.id}/${geronimo.war.artifact.id}/${geronimo.war.version}/${geronimo.war.type}"/> + + <property name="geronimo.ear.group.id" value="qpid.jca.example"/> + <property name="geronimo.ear.artifact.id" value="QpidJCAEARExample"/> + <property name="geronimo.ear.version" value="1.0"/> + <property name="geronimo.ear.type" value="ear"/> + <property name="geronimo.ear.id" value="${geronimo.ear.group.id}/${geronimo.ear.artifact.id}/${geronimo.ear.version}/${geronimo.ear.type}"/> + + <property name="geronimo.rar.plan" value="${gen.dir}/geronimo-ra.xml"/> + <property name="geronimo.ear.plan" value="${gen.dir}/geronimo-application.xml"/> + + <path id="compile.classpath"> + <fileset dir="${geronimo.home}/repository/org/apache/geronimo/specs"> + <include name="geronimo-jms_1.1_spec/1.1.1/geronimo-jms_1.1_spec-1.1.1.jar"/> + <include name="geronimo-servlet_2.5_spec/1.2/geronimo-servlet_2.5_spec-1.2.jar"/> + <include name="geronimo-ejb_3.0_spec/1.0.1/geronimo-ejb_3.0_spec-1.0.1.jar"/> + <include name="geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar"/> + </fileset> + <fileset dir="${geronimo.home}/lib/"> + <include name="slf4j-api-*.jar"/> + </fileset> + </path> + + <path id="run.classpath"> + <fileset dir="${lib.dir}"> + <include name="qpid-ra-*.jar"/> + <include name="qpid-client-*.jar"/> + <include name="qpid-common-*.jar"/> + </fileset> + <fileset dir="${geronimo.home}/repository/org/apache/geronimo/specs"> + <include name="geronimo-j2ee-connector_1.5_spec/2.0.0/geronimo-j2ee-connector_1.5_spec-2.0.0.jar"/> + <include name="geronimo-jms_1.1_spec/1.1.1/geronimo-jms_1.1_spec-1.1.1.jar"/> + <include name="geronimo-servlet_2.5_spec/1.2/geronimo-servlet_2.5_spec-1.2.jar"/> + <include name="geronimo-ejb_3.0_spec/1.0.1/geronimo-ejb_3.0_spec-1.0.1.jar"/> + <include name="geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar"/> + </fileset> + <fileset dir="${geronimo.home}/lib/"> + <include name="slf4j-api-*.jar"/> + <include name="slf4j-log4j*-*.jar"/> + <include name="log4j-*.jar"/> + </fileset> + <fileset dir="${geronimo.home}/repository/org/apache/openejb/openejb-client/3.1.4/"/> + </path> + + <filterset id="extra.filterset"> + <filter token="geronimo.ejb.group.id" value="${geronimo.ejb.group.id}"/> + <filter token="geronimo.ejb.artifact.id" value="${geronimo.ejb.artifact.id}"/> + <filter token="geronimo.ejb.version" value="${geronimo.ejb.version}"/> + <filter token="geronimo.ejb.type" value="${geronimo.ejb.type}"/> + <filter token="geronimo.war.group.id" value="${geronimo.war.group.id}"/> + <filter token="geronimo.war.artifact.id" value="${geronimo.war.artifact.id}"/> + <filter token="geronimo.war.version" value="${geronimo.war.version}"/> + <filter token="geronimo.war.type" value="${geronimo.war.type}"/> + <filter token="geronimo.ear.group.id" value="${geronimo.ear.group.id}"/> + <filter token="geronimo.ear.artifact.id" value="${geronimo.ear.artifact.id}"/> + <filter token="geronimo.ear.version" value="${geronimo.ear.version}"/> + <filter token="geronimo.ear.type" value="${geronimo.ear.type}"/> + </filterset> + + <macrodef name="geronimo"> + <attribute name="user" default="${geronimo.user}"/> + <attribute name="password" default="${geronimo.password}"/> + <attribute name="action" default="list-modules"/> + <attribute name="module"/> + <attribute name="plan" default=""/> + <sequential> + <exec executable="${geronimo.home}/bin/deploy.sh"> + <arg line="-u @{user} -p @{password} @{action} @{module} @{plan}"/> + </exec> + </sequential> + </macrodef> + + <!-- Deployment is target specific so is included here --> + <target name="deploy-rar" depends="generate" description="Deploy the RAR file."> + <geronimo action="deploy" module="${qpid.jca.dir}/${rar.name}" plan="${geronimo.rar.plan}"/> + </target> + + <target name="undeploy-rar" description="Undeploys the RAR deployment."> + <geronimo action="undeploy" module="${geronimo.rar.id}"/> + </target> + + <target name="start-rar" description="Starts the RAR deployment in the Geronimo environment."> + <geronimo action="start" module="${geronimo.rar.id}"/> + </target> + + <target name="stop-rar" description="Stops the RAR deployment in the Geronimo environment."> + <geronimo action="stop" module="${geronimo.rar.id}"/> + </target> + + <target name="restart-rar" description="Restarts the RAR deployment in the Geronimo environment."> + <geronimo action="restart" module="${geronimo.rar.id}"/> + </target> + + <target name="deploy-ear" depends="package-ear" description="Deploys the EAR archive."> + <geronimo action="deploy" module="${build.dir}/${ear.name}" plan="${geronimo.ear.plan}"/> + </target> + + <target name="undeploy-ear" description="Undeployes the EAR archive."> + <geronimo action="undeploy" module="${geronimo.ear.id}"/> + </target> + + <target name="start-ear" description="Starts the EAR deployment in the Geronimo environment."> + <geronimo action="start" module="${geronimo.ear.id}"/> + </target> + + <target name="stop-ear" description="Stops the EAR deployment in the Geronimo environment."> + <geronimo action="stop" module="${geronimo.ear.id}"/> + </target> + + <target name="restart-ear" description="Restarts the EAR deployment in the Geronimo environment."> + <geronimo action="restart" module="${geronimo.ear.id}"/> + </target> + +</project> diff --git a/qpid/java/jca/example/build-jboss-properties.xml b/qpid/java/jca/example/build-jboss-properties.xml new file mode 100644 index 0000000000..3554488d2d --- /dev/null +++ b/qpid/java/jca/example/build-jboss-properties.xml @@ -0,0 +1,115 @@ +<!-- + - + - 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. + - +--> +<project name="qpid-jca-example-jboss-properties" basedir="." default=""> + + <property name="jboss.jndi.scheme" value="mappedName"/> + <property name="qpid.xacf.jndi.name" value="java:QpidJMSXA"/> + <property name="qpid.cf.jndi.name" value="QpidConnectionFactory"/> + <property name="qpid.hello.topic.jndi.name" value="HelloTopic"/> + <property name="qpid.goodbye.topic.jndi.name" value="GoodByeTopic"/> + <property name="qpid.hello.queue.jndi.name" value="HelloQueue"/> + <property name="qpid.goodbye.queue.jndi.name" value="GoodByeQueue"/> + <property name="qpid.responder.queue.jndi.name" value="QpidResponderQueue"/> + <property name="qpid.ejb.jndi.name" value="mappedName="QpidTestEJB""/> + <property name="qpid.ejb.ref.name" value="QpidTestBean/local"/> + <property name="qpid.ejb.name" value="qpid-jcaex/QpidTestBean/remote"/> + + <property name="jndi.context" value="org.jnp.interfaces.NamingContextFactory"/> + <property name="server.host" value="jnp://localhost:1099"/> + + <property name="jboss.home" location="${env.JBOSS_HOME}"/> + <property name="jboss.server" value="default"/> + <property name="jboss.deploy" location="${jboss.home}/server/${jboss.server}/deploy"/> + <property name="jboss.client" location="${jboss.home}/client"/> + + <path id="compile.classpath"> + <fileset dir="${jboss.client}"> + <!-- JBoss 5--> + <include name="jboss-javaee.jar"/> + + <!-- JBoss 6 --> + <include name="jboss-servlet-api_3.0_spec.jar"/> + <include name="jboss-jms-api_1.1_spec.jar"/> + <include name="jboss-ejb-api_3.1_spec.jar"/> + <include name="jboss-transaction-api_1.1_spec.jar"/> + + <!-- Common to JBoss 5/6 --> + <include name="slf4j-api.jar"/> + </fileset> + + <!-- JBoss 5 --> + <fileset dir="${jboss.home}/common/lib"> + <include name="servlet-api.jar"/> + </fileset> + </path> + + <path id="run.classpath"> + <fileset dir="${lib.dir}"> + <include name="qpid-ra-*.jar"/> + <include name="qpid-client-*.jar"/> + <include name="qpid-common-*.jar"/> + </fileset> + <fileset dir="${jboss.client}"> + <!-- Shortcut to get it working!--> + <include name="jbossall-client.jar"/> + </fileset> + </path> + + <filterset id="extra.filterset"/> + + <!-- Deployment is target specific so is included here --> + <target name="deploy-rar" description="Deploy the RAR file."> + <copy todir="${jboss.deploy}" overwrite="true"> + <fileset dir="${qpid.jca.dir}"> + <include name="${rar.name}"/> + </fileset> + </copy> + </target> + + <target name="undeploy-rar" description="Undeploys the RAR deployment."> + <delete file="${jboss.deploy}/${rar.name}"/> + </target> + + <target name="deploy-ear" depends="package-ear" description="Deploys the EAR archive."> + <copy todir="${jboss.deploy}" overwrite="true"> + <fileset dir="${build.dir}"> + <include name="${ear.name}"/> + </fileset> + </copy> + </target> + + <target name="undeploy-ear" description="Undeploys the EAR archive."> + <delete file="${jboss.deploy}/${ear.name}"/> + </target> + + <target name="deploy-ds" depends="generate" description="Deploys the ds.xml file to the JBoss environment."> + <copy todir="${jboss.deploy}" overwrite="true"> + <fileset dir="${gen.dir}"> + <include name="qpid-jca-ds.xml"/> + </fileset> + </copy> + </target> + + <target name="undeploy-ds" description="Undeploys the ds.xml file from the JBoss environment."> + <delete file="${jboss.deploy}/qpid-jca-ds.xml"/> + </target> + +</project> diff --git a/qpid/java/systests/etc/config-systests-aclv2-settings.xml b/qpid/java/jca/example/build-properties.xml.temp index fbf218fdfa..9eae869c27 100644 --- a/qpid/java/systests/etc/config-systests-aclv2-settings.xml +++ b/qpid/java/jca/example/build-properties.xml.temp @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> <!-- - - Licensed to the Apache Software Foundation (ASF) under one @@ -19,12 +18,6 @@ - under the License. - --> -<broker> - <security> - <aclv2>${QPID_HOME}/etc/global-default.txt</aclv2> - </security> - - <virtualhosts>${QPID_HOME}/etc/virtualhosts-systests-aclv2.xml</virtualhosts> -</broker> - - +<project name="qpid-jca-example-build-properties" basedir="." default=""> + <property name="qpid.ver" value="@project.version@"/> +</project> diff --git a/qpid/java/jca/example/build.xml b/qpid/java/jca/example/build.xml new file mode 100644 index 0000000000..9d0cfc887e --- /dev/null +++ b/qpid/java/jca/example/build.xml @@ -0,0 +1,204 @@ +<!-- + - + - 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. + - + --> +<project name="qpid-jca-example" default="help" basedir=""> + + <!-- Valid target platforms are currently geronimo & jboss --> + <property name="target.platform" value="geronimo"/> + + <!-- Change to BURL for older syntax support --> + <property name="qpid.dest_syntax" value="ADDR"/> + + <!-- Broker specific properties. By default in the adapter we use localhost here you an override this with host specific info--> + <property name="broker.address" value="localhost"/> + + <!-- Properties controlling running sample standalone client --> + <property name="client.use.ejb" value="false"/> <!-- uses JNDI/JMS or JNDI/RMI --> + <property name="client.message" value="Hello Qpid World"/> + <property name="client.message.count" value="1"/> + <property name="client.use.topic" value="false"/> <!-- Use topic/queue --> + <property name="client.say.goodbye" value="false"/> + + <!-- Pull in environment vars as properties --> + <property environment="env"/> + + <!-- QPID version property --> + <import file="${basedir}/build-properties.xml"/> + + <import file="${basedir}/qpid-jca-example-properties.xml"/> + + <!-- Target specific properties/targets --> + <import file="${basedir}/build-${target.platform}-properties.xml"/> + + <macrodef name="compile"> + <attribute name="classpath"/> + <sequential> + <javac srcdir="${gen.dir}" + destdir="${build.classes.dir}" + classpathref="@{classpath}" + debug="true" optimize="false"/> + </sequential> + </macrodef> + + <echo message="Using Qpid version ${qpid.ver}"/> + <echo message="Building for platform ${target.platform}"/> + <echo message="Broker url is currently set to ${broker.url}"/> + <echo message="Qpid Destination Syntax is ${qpid.dest_syntax}"/> + + <target name="init"> + <mkdir dir="${build.classes.dir}"/> + <mkdir dir="${gen.dir}"/> + <mkdir dir="${log.dir}"/> + </target> + + <target name="generate" depends="init"> + <copy todir="${gen.dir}" overwrite="true"> + <fileset dir="${conf.dir}"/> + <filterset> + <filter token="rar.name" value="${rar.name}"/> + <filter token="ejb.name" value="${ejb.name}"/> + <filter token="war.name" value="${war.name}"/> + + <filter token="broker.url" value="${broker.url}"/> + + <filter token="qpid.hello.topic.dest.address" value="${qpid.hello.topic.dest.address}"/> + <filter token="qpid.goodbye.topic.dest.address" value="${qpid.goodbye.topic.dest.address}"/> + <filter token="qpid.hellogoodbye.topic.dest.address" value="${qpid.hellogoodbye.topic.dest.address}"/> + <filter token="qpid.hello.queue.dest.address" value="${qpid.hello.queue.dest.address}"/> + <filter token="qpid.goodbye.queue.dest.address" value="${qpid.goodbye.queue.dest.address}"/> + <filter token="qpid.responder.queue.dest.address" value="${qpid.responder.queue.dest.address}"/> + + </filterset> + <filterset refid="extra.filterset"/> + </copy> + <copy todir="${gen.dir}"> + <fileset dir="${src.dir}"/> + <filterset> + <filter token="rar.name" value="${rar.name}"/> + <filter token="broker.url" value="${broker.url}"/> + <filter token="jndi.scheme" value="${jndi.scheme}"/> + <filter token="qpid.xacf.jndi.name" value="${qpid.xacf.jndi.name}"/> + <filter token="qpid.hello.topic.jndi.name" value="${qpid.hello.topic.jndi.name}"/> + <filter token="qpid.goodbye.topic.jndi.name" value="${qpid.goodbye.topic.jndi.name}"/> + <filter token="qpid.hello.queue.jndi.name" value="${qpid.hello.queue.jndi.name}"/> + <filter token="qpid.goodbye.queue.jndi.name" value="${qpid.goodbye.queue.jndi.name}"/> + <filter token="qpid.responder.queue.jndi.name" value="${qpid.responder.queue.jndi.name}"/> + <filter token="qpid.ejb.jndi.name" value="${qpid.ejb.jndi.name}"/> + </filterset> + </copy> + </target> + + <target name="compile" depends="generate" description="Compiles the source files for the Qpid JCA example"> + <compile classpath="compile.classpath"/> + </target> + + <target name="package-war" depends="compile" description="Packages the WAR file for deployment."> + <war destfile="${build.dir}/${war.name}" webxml="${gen.dir}/web.xml"> + <classes dir="${build.classes.dir}"> + <include name="org/apache/qpid/jca/example/web/**"/> + </classes> + <webinf dir="${gen.dir}"> + <include name="jboss-web.xml"/> + </webinf> + </war> + </target> + + <target name="package-ejb" depends="compile" description="Packages the EJB archive for deployment."> + <jar destfile="${build.dir}/${ejb.name}" basedir="${build.classes.dir}"> + <include name="org/apache/qpid/jca/example/ejb/**/*.class"/> + <metainf dir="${gen.dir}"> + <include name="jboss.xml"/> + <include name="ejb-jar.xml"/> + </metainf> + </jar> + </target> + + <target name="package-ear" depends="generate, package-war, package-ejb" description="Packages the EAR archive for deployment."> + <jar destfile="${build.dir}/${ear.name}" basedir="${build.dir}"> + <include name="*.war"/> + <include name="*.jar"/> + <metainf dir="${gen.dir}"> + <include name="application.xml"/> + </metainf> + </jar> + </target> + + <target name="run-client" depends="compile" description="Runs the RMI client."> + <java classname="org.apache.qpid.jca.example.client.QpidTestClient"> + <classpath> + <pathelement path="${build.classes.dir}"/> + <path refid="run.classpath"/> + </classpath> + <sysproperty key="java.naming.factory.initial" value="${jndi.context}"/> + <sysproperty key="java.naming.provider.url" value="${server.host}"/> + <sysproperty key="qpid.ejb.name" value="${qpid.ejb.name}"/> + <sysproperty key="qpid.cf.name" value="${qpid.cf.jndi.name}"/> + <sysproperty key="qpid.dest_syntax" value="${qpid.dest_syntax}"/> + <sysproperty key="qpid.dest.name" value="${qpid.hello.queue.jndi.name}"/> + <sysproperty key="log4j.configuration" value="file://${conf.dir}/log4j.properties"/> + + <sysproperty key="qpid.message" value="${client.message}"/> + <sysproperty key="message.count" value="${client.message.count}"/> + <sysproperty key="use.topic" value="${client.use.topic}"/> + <sysproperty key="use.ejb" value="${client.use.ejb}"/> + <sysproperty key="say.goodbye" value="${client.say.goodbye}"/> + </java> + </target> + + <target name="run-reqresp" depends="compile"> + <java classname="org.apache.qpid.jca.example.client.QpidRequestResponseClient"> + <classpath> + <pathelement path="${build.classes.dir}"/> + <path refid="run.classpath"/> + </classpath> + <sysproperty key="java.naming.factory.initial" value="${jndi.context}"/> + <sysproperty key="java.naming.provider.url" value="${server.host}"/> + <sysproperty key="qpid.message" value="Hello, World"/> + <sysproperty key="message.count" value="1"/> + <sysproperty key="thread.count" value="5"/> + <sysproperty key="qpid.cf.name" value="${qpid.cf.jndi.name}"/> + <sysproperty key="qpid.dest.name" value="${qpid.responder.queue.jndi.name}"/> + <sysproperty key="log4j.configuration" value="file://${conf.dir}/log4j.properties"/> + <sysproperty key="qpid.dest_syntax" value="${qpid.dest_syntax}"/> + </java> + </target> + + <target name="clean" description="Deletes the build directory and all related files."> + <delete dir="${build.dir}"/> + </target> + + <target name="help"> + <echo> + + ant compile + This will compile all the source code for the Qpid JCA example project to the ${build.classes.dir} directory. + + ant deploy-rar deploy-ear + Deploys a particular component which could be rar, ear (or ds for JBoss) + + ant undeploy-ear undeploy-rar + Undeploys a particular component which could be rar, ear (or ds for JBoss) + + ant run-client run-reqresp + Runs the RMI/thin client or the request-response client example + </echo> + </target> + +</project> diff --git a/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml b/qpid/java/jca/example/conf/application.xml index db396d7ab1..d181bcda67 100644 --- a/qpid/java/systests/etc/virtualhosts-systests-aclv2.xml +++ b/qpid/java/jca/example/conf/application.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> +<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed to the Apache Software Foundation (ASF) under one @@ -19,11 +19,19 @@ - under the License. - --> -<configuration> - <system/> - <override> - <xml fileName="${QPID_HOME}/${test.virtualhosts}" optional="true"/> - <xml fileName="${QPID_HOME}/etc/virtualhosts-systests-aclv2-settings.xml"/> - <xml fileName="${QPID_HOME}/etc/virtualhosts.xml"/> - </override> -</configuration> +<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"> + + <module> + <ejb>@ejb.name@</ejb> + </module> + + <module> + <web> + <web-uri>@war.name@</web-uri> + <context-root>/qpid-jca-web</context-root> + </web> + </module> + +</application> + diff --git a/qpid/java/jca/example/conf/ejb-jar.xml b/qpid/java/jca/example/conf/ejb-jar.xml new file mode 100644 index 0000000000..2f513bd3f8 --- /dev/null +++ b/qpid/java/jca/example/conf/ejb-jar.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + - + - 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. + - + --> +<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee + http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"> + + <enterprise-beans> + <message-driven> + <ejb-name>QpidHelloListenerBean</ejb-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <res-auth>Container</res-auth> + <res-sharing-scope>Shareable</res-sharing-scope> + </resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidHelloSubscriberBean</ejb-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <res-auth>Container</res-auth> + <res-sharing-scope>Shareable</res-sharing-scope> + </resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidJMSResponderBean</ejb-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <res-auth>Container</res-auth> + <res-sharing-scope>Shareable</res-sharing-scope> + </resource-ref> + </message-driven> + <session> + <ejb-name>QpidTestBean</ejb-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <res-auth>Container</res-auth> + <res-sharing-scope>Shareable</res-sharing-scope> + </resource-ref> + </session> + </enterprise-beans> + + +</ejb-jar> diff --git a/qpid/java/jca/example/conf/geronimo-application.xml b/qpid/java/jca/example/conf/geronimo-application.xml new file mode 100644 index 0000000000..832496e76f --- /dev/null +++ b/qpid/java/jca/example/conf/geronimo-application.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - + - 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. + - + --> +<application xmlns="http://geronimo.apache.org/xml/ns/j2ee/application-2.0" + xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.2" + xmlns:naming="http://geronimo.apache.org/xml/ns/naming-1.2" + application-name="QpidJCAExampleApplication"> + + <sys:environment> + <sys:moduleId> + <sys:groupId>@geronimo.ear.group.id@</sys:groupId> + <sys:artifactId>@geronimo.ear.artifact.id@</sys:artifactId> + <sys:version>@geronimo.ear.version@</sys:version> + <sys:type>@geronimo.ear.type@</sys:type> + </sys:moduleId> + </sys:environment> + + <!-- Plan for embedded WAR --> + <module> + <web>qpid-jcaex-web.war</web> + <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1"> + + <sys:environment> + <sys:moduleId> + <sys:groupId>@geronimo.war.group.id@</sys:groupId> + <sys:artifactId>@geronimo.war.artifact.id@</sys:artifactId> + <sys:version>@geronimo.war.version@</sys:version> + <sys:type>@geronimo.war.type@</sys:type> + </sys:moduleId> + + <sys:dependencies> + <sys:dependency> + <sys:groupId>qpid.jca</sys:groupId> + <sys:artifactId>QpidJCAAdapter</sys:artifactId> + <sys:version>1.0</sys:version> + <sys:type>rar</sys:type> + </sys:dependency> + </sys:dependencies> + </sys:environment> + + <context-root>/qpid-jca-web</context-root> + + <naming:ejb-local-ref> + <naming:ref-name>QpidTestBean</naming:ref-name> + <naming:ejb-link>QpidTestBean</naming:ejb-link> + </naming:ejb-local-ref> + + <naming:resource-ref> + <naming:ref-name>QpidJMSXA</naming:ref-name> + <naming:resource-link>QpidJMSXA</naming:resource-link> + </naming:resource-ref> + </web-app> + </module> + + <!-- Plan for embedded EJBs --> + <module> + <ejb>qpid-jcaex-ejb.jar</ejb> + <openejb-jar xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1"> + + <sys:environment> + <sys:moduleId> + <sys:groupId>@geronimo.ejb.group.id@</sys:groupId> + <sys:artifactId>@geronimo.ejb.artifact.id@</sys:artifactId> + <sys:version>@geronimo.ejb.version@</sys:version> + <sys:type>@geronimo.ejb.type@</sys:type> + </sys:moduleId> + + <sys:dependencies> + <sys:dependency> + <sys:groupId>qpid.jca</sys:groupId> + <sys:artifactId>QpidJCAAdapter</sys:artifactId> + <sys:version>1.0</sys:version> + <sys:type>rar</sys:type> + </sys:dependency> + </sys:dependencies> + <sys:hidden-classes/> + <sys:non-overridable-classes/> + </sys:environment> + + <enterprise-beans> + <message-driven> + <ejb-name>QpidHelloListenerBean</ejb-name> + <resource-adapter> + <resource-link>QpidResourceAdapter</resource-link> + </resource-adapter> + <naming:resource-ref> + <naming:ref-name>QpidJMSXA</naming:ref-name> + <naming:resource-link>QpidJMSXA</naming:resource-link> + </naming:resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidGoodByeListenerBean</ejb-name> + <resource-adapter> + <resource-link>QpidResourceAdapter</resource-link> + </resource-adapter> + </message-driven> + <message-driven> + <ejb-name>QpidHelloSubscriberBean</ejb-name> + <resource-adapter> + <resource-link>QpidResourceAdapter</resource-link> + </resource-adapter> + <naming:resource-ref> + <naming:ref-name>QpidJMSXA</naming:ref-name> + <naming:resource-link>QpidJMSXA</naming:resource-link> + </naming:resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidGoodByeSubscriberBean</ejb-name> + <resource-adapter> + <resource-link>QpidResourceAdapter</resource-link> + </resource-adapter> + </message-driven> + <message-driven> + <ejb-name>QpidJMSResponderBean</ejb-name> + <resource-adapter> + <resource-link>QpidResourceAdapter</resource-link> + </resource-adapter> + <naming:resource-ref> + <naming:ref-name>QpidJMSXA</naming:ref-name> + <naming:resource-link>QpidJMSXA</naming:resource-link> + </naming:resource-ref> + </message-driven> + <session> + <ejb-name>QpidTestBean</ejb-name> + <naming:resource-ref> + <naming:ref-name>QpidJMSXA</naming:ref-name> + <naming:resource-link>QpidJMSXA</naming:resource-link> + </naming:resource-ref> + </session> + </enterprise-beans> + </openejb-jar> + </module> +</application> diff --git a/qpid/java/jca/example/conf/geronimo-ra.xml b/qpid/java/jca/example/conf/geronimo-ra.xml new file mode 100644 index 0000000000..2943ac0a58 --- /dev/null +++ b/qpid/java/jca/example/conf/geronimo-ra.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - + - 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. + - + --> +<connector xmlns="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2"> + <dep:environment xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2"> + <dep:moduleId> + <dep:groupId>qpid.jca</dep:groupId> + <dep:artifactId>QpidJCAAdapter</dep:artifactId> + <dep:version>1.0</dep:version> + <dep:type>rar</dep:type> + </dep:moduleId> + </dep:environment> + <resourceadapter> + <resourceadapter-instance> + <resourceadapter-name>QpidResourceAdapter</resourceadapter-name> + <config-property-setting name="ClientId">client_id</config-property-setting> + <config-property-setting name="TransactionManagerLocatorClass">org.apache.qpid.ra.tm.GeronimoTransactionManagerLocator</config-property-setting> + <config-property-setting name="TransactionManagerLocatorMethod">getTransactionManager</config-property-setting> + <!-- Note, currently there is a bug with end/suspend and Geronimo. For now use local transactions--> + <config-property-setting name="UseLocalTx">true</config-property-setting> + <workmanager> + <gbean-link>DefaultWorkManager</gbean-link> + </workmanager> + </resourceadapter-instance> + <outbound-resourceadapter> + <connection-definition> + <connectionfactory-interface>org.apache.qpid.ra.QpidRAConnectionFactory</connectionfactory-interface> + <connectiondefinition-instance> + <name>QpidJMSXA</name> + <implemented-interface>javax.jms.QueueConnectionFactory</implemented-interface> + <implemented-interface>javax.jms.TopicConnectionFactory</implemented-interface> + <config-property-setting name="ConnectionURL">@broker.url@</config-property-setting> + <connectionmanager> + <!-- Note, currently there is a bug with end/suspend and Geronimo. For now use no transactions outbound --> + <no-transaction/> + <single-pool> + <max-size>20</max-size> + <min-size>0</min-size> + <match-one/> + </single-pool> + </connectionmanager> + </connectiondefinition-instance> + </connection-definition> + </outbound-resourceadapter> + <!-- Note, do not remove this admin object. There appears to be a bug in Geronimo's deployer that does not correctly create JNDI references + if an extra admin object is not present --> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>Dummy</message-destination-name> + <config-property-setting name="destinationType">TOPIC</config-property-setting> + <config-property-setting name="destinationAddress">amq.topic</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>HelloTopic</message-destination-name> + <config-property-setting name="destinationType">TOPIC</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.hello.topic.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>GoodByeTopic</message-destination-name> + <config-property-setting name="destinationType">TOPIC</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.goodbye.topic.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>HelloGoodByeTopic</message-destination-name> + <config-property-setting name="destinationType">TOPIC</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.hellogoodbye.topic.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>HelloQueue</message-destination-name> + <config-property-setting name="destinationType">QUEUE</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.hello.queue.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>GoodByeQueue</message-destination-name> + <config-property-setting name="destinationType">QUEUE</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.goodbye.queue.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>ResponderQueue</message-destination-name> + <config-property-setting name="destinationType">QUEUE</config-property-setting> + <config-property-setting name="destinationAddress">@qpid.responder.queue.dest.address@</config-property-setting> + </adminobject-instance> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.ConnectionFactory</adminobject-interface> + <adminobject-class>org.apache.qpid.ra.admin.QpidConnectionFactoryProxy</adminobject-class> + <adminobject-instance> + <message-destination-name>QpidConnectionFactory</message-destination-name> + <config-property-setting name="connectionURL">@broker.url@</config-property-setting> + </adminobject-instance> + </adminobject> + </resourceadapter> +</connector> + diff --git a/qpid/java/systests/etc/config-systests-aclv2.xml b/qpid/java/jca/example/conf/jboss-web.xml index e8b971a2a0..edacf8d418 100644 --- a/qpid/java/systests/etc/config-systests-aclv2.xml +++ b/qpid/java/jca/example/conf/jboss-web.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> +<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed to the Apache Software Foundation (ASF) under one @@ -19,12 +19,16 @@ - under the License. - --> -<configuration> - <system/> - <override> - <xml fileName="${QPID_HOME}/${test.config}" optional="true"/> - <xml fileName="${QPID_HOME}/etc/config-systests-aclv2-settings.xml"/> - <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> - <xml fileName="${QPID_HOME}/etc/config.xml"/> - </override> -</configuration> +<jboss-web> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + <ejb-local-ref> + <ejb-ref-name>QpidTestBean</ejb-ref-name> + <jndi-name>qpid-jcaex/QpidTestBean/local</jndi-name> + </ejb-local-ref> + <context-root>qpid-jca-web</context-root> +</jboss-web> + diff --git a/qpid/java/jca/example/conf/jboss.xml b/qpid/java/jca/example/conf/jboss.xml new file mode 100644 index 0000000000..8b62ca73b0 --- /dev/null +++ b/qpid/java/jca/example/conf/jboss.xml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<!-- + - + - 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. + - + --> +<jboss + xmlns="http://www.jboss.com/xml/ns/javaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee + http://www.jboss.org/j2ee/schema/jboss_5_0.xsd" + version="3.0"> + + <enterprise-beans> + <message-driven> + <ejb-name>QpidHelloListenerBean</ejb-name> + <resource-adapter-name>@rar.name@</resource-adapter-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.sql.DataSource</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidGoodByeListenerBean</ejb-name> + <resource-adapter-name>@rar.name@</resource-adapter-name> + </message-driven> + <message-driven> + <ejb-name>QpidHelloSubscriberBean</ejb-name> + <resource-adapter-name>@rar.name@</resource-adapter-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.sql.DataSource</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidGoodByeSubscriberBean</ejb-name> + <resource-adapter-name>@rar.name@</resource-adapter-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.sql.DataSource</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + </message-driven> + <message-driven> + <ejb-name>QpidJMSResponderBean</ejb-name> + <resource-adapter-name>@rar.name@</resource-adapter-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.sql.DataSource</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + </message-driven> + <session> + <ejb-name>QpidTestBean</ejb-name> + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.sql.DataSource</res-type> + <jndi-name>java:/QpidJMSXA</jndi-name> + </resource-ref> + </session> + </enterprise-beans> + +</jboss> diff --git a/qpid/java/jca/example/conf/log4j.properties b/qpid/java/jca/example/conf/log4j.properties new file mode 100644 index 0000000000..f1847f4418 --- /dev/null +++ b/qpid/java/jca/example/conf/log4j.properties @@ -0,0 +1,18 @@ +log4j.rootLogger=DEBUG, CONSOLE, FILE + +#Console Appender +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n + +#File Appender +log4j.appender.FILE=org.apache.log4j.FileAppender +log4j.appender.FILE.File=./build/log/qpid-jca-example.log +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n + +log4j.logger.org.jboss=WARN +log4j.logger.org.jnp.interfaces=WARN +log4j.logger.org.apache.qpid=ERROR +log4j.logger.org.apache.qpid.jca.example=DEBUG + diff --git a/qpid/java/jca/example/conf/qpid-jca-ds.xml b/qpid/java/jca/example/conf/qpid-jca-ds.xml new file mode 100644 index 0000000000..9e589169e3 --- /dev/null +++ b/qpid/java/jca/example/conf/qpid-jca-ds.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - + - 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. + - + --> +<connection-factories> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=HelloTopic"> + <attribute name="JNDIName">HelloTopic</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=TOPIC + destinationAddress=@qpid.hello.topic.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=GoodByeTopic"> + <attribute name="JNDIName">GoodByeTopic</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=TOPIC + destinationAddress=@qpid.goodbye.topic.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=HelloGoodByeTopic"> + <attribute name="JNDIName">HelloGoodByeTopic</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=TOPIC + destinationAddress=@qpid.hellogoodbye.topic.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=HelloQueue"> + <attribute name="JNDIName">HelloQueue</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=QUEUE + destinationAddress=@qpid.hello.queue.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=GoodByeQueue"> + <attribute name="JNDIName">GoodByeQueue</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=QUEUE + destinationAddress=@qpid.goodbye.queue.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=QpidResponderQueue"> + <attribute name="JNDIName">QpidResponderQueue</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.Destination</attribute> + <attribute name="Properties"> + destinationType=QUEUE + destinationAddress=@qpid.responder.queue.dest.address@ + </attribute> + </mbean> + + <mbean code="org.jboss.resource.deployment.AdminObject" + name="qpid.jca:name=QpidConnectionFactory"> + <attribute name="JNDIName">QpidConnectionFactory</attribute> + <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='@rar.name@'</depends> + <attribute name="Type">javax.jms.ConnectionFactory</attribute> + <attribute name="Properties"> + connectionURL=@broker.url@ + </attribute> + </mbean> + + <!-- Non XA connection factory. Can be used when running adapter against clustered Brokers --> + <tx-connection-factory> + <jndi-name>QpidJMS</jndi-name> + <rar-name>@rar.name@</rar-name> + <local-transaction/> + <config-property name="useLocalTx" type="java.lang.Boolean">true</config-property> + <config-property name="connectionURL">@broker.url@</config-property> + <config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Queue</config-property> + <connection-definition>org.apache.qpid.ra.QpidRAConnectionFactory</connection-definition> + <max-pool-size>20</max-pool-size> + </tx-connection-factory> + + <!--XA ConnectionFactory--> + <tx-connection-factory> + <jndi-name>QpidJMSXA</jndi-name> + <xa-transaction/> + <rar-name>@rar.name@</rar-name> + <config-property name="connectionURL">@broker.url@</config-property> + <config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Queue</config-property> + <connection-definition>org.apache.qpid.ra.QpidRAConnectionFactory</connection-definition> + <max-pool-size>20</max-pool-size> + </tx-connection-factory> + +</connection-factories> diff --git a/qpid/java/jca/example/conf/web.xml b/qpid/java/jca/example/conf/web.xml new file mode 100644 index 0000000000..509612dc90 --- /dev/null +++ b/qpid/java/jca/example/conf/web.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - + - 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. + - + --> +<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> + + <resource-ref> + <res-ref-name>QpidJMSXA</res-ref-name> + <res-type>javax.jms.ConnectionFactory</res-type> + <res-auth>Container</res-auth> + <res-sharing-scope>Shareable</res-sharing-scope> + </resource-ref> + + <ejb-local-ref> + <ejb-ref-name>QpidTestBean</ejb-ref-name> + <ejb-ref-type>Session</ejb-ref-type> + <local>org.apache.qpid.jca.example.ejb.QpidTestLocal</local> + </ejb-local-ref> + + <servlet> + <display-name>QpidTestServlet</display-name> + <servlet-name>QpidTestServlet</servlet-name> + <servlet-class>org.apache.qpid.jca.example.web.QpidTestServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>QpidTestServlet</servlet-name> + <url-pattern>/qpid</url-pattern> + </servlet-mapping> + + +</web-app> + diff --git a/qpid/java/jca/example/qpid-jca-example-properties.xml b/qpid/java/jca/example/qpid-jca-example-properties.xml new file mode 100644 index 0000000000..ee0478a6e5 --- /dev/null +++ b/qpid/java/jca/example/qpid-jca-example-properties.xml @@ -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. + - + --> +<project name="qpid-jca-example-properties" basedir="." default=""> + + <property name="src.dir" location="${basedir}/src/main/java"/> + <property name="lib.dir" location="${basedir}/lib"/> + <property name="conf.dir" location="${basedir}/conf"/> + <property name="build.dir" location="${basedir}/build"/> + <property name="build.classes.dir" location="${build.dir}/classes"/> + <property name="gen.dir" location="${build.dir}/gen"/> + <property name="log.dir" location="${build.dir}/log"/> + <property name="qpid.jca.dir" location="${env.QPID_JCA_HOME}"/> + + <property name="ejb.name" value="qpid-jcaex-ejb.jar"/> + <property name="war.name" value="qpid-jcaex-web.war"/> + <property name="ear.name" value="qpid-jcaex.ear"/> + + <property name="rar.ver" value="${qpid.ver}"/> + <property name="rar.name" value="qpid-ra-${rar.ver}.rar"/> + + <property name="broker.url" value="amqp://anonymous:passwd@client/test?brokerlist='tcp://${broker.address}?sasl_mechs='ANONYMOUS''"/> + + <property name="qpid.hello.topic.dest.address.ADDR" value="amq.topic/hello.Topic"/> + <property name="qpid.goodbye.topic.dest.address.ADDR" value="amq.topic/goodbye.Topic"/> + <property name="qpid.hellogoodbye.topic.dest.address.ADDR" value="amq.topic/goodbye.Topic"/> + <property name="qpid.hello.queue.dest.address.ADDR" + value="hello.Queue;{create:always, node:{type:queue, x-declare:{auto-delete:true}}}"/> + <property name="qpid.goodbye.queue.dest.address.ADDR" + value="goodbye.Queue;{create:always, node:{type:queue, x-declare:{auto-delete:true}}}"/> + <property name="qpid.responder.queue.dest.address.ADDR" + value="responder.Queue;{create:always, node:{type:queue, x-declare:{auto-delete:true}}}"/> + + <property name="qpid.hello.topic.dest.address.BURL" + value="topic://amq.topic//hello.jcaTopic?routingKey='hello.jcaTopic',autodelete='true'"/> + <property name="qpid.goodbye.topic.dest.address.BURL" + value="topic://amq.topic//goodbye.jcaTopic?routingKey='goodbye.jcaTopic',autodelete='true'"/> + <property name="qpid.hellogoodbye.topic.dest.address.BURL" + value="topic://amq.topic//#.jcaTopic"/> + <property name="qpid.hello.queue.dest.address.BURL" + value="direct://amq.direct//hello.Queue?routingkey='hello.Queue'"/> + <property name="qpid.goodbye.queue.dest.address.BURL" + value="direct://amq.direct//goodbye.Queue?routingkey='goodbye.Queue'"/> + <property name="qpid.responder.queue.dest.address.BURL" + value="direct://amq.direct//responder.Queue?routingkey='responder.Queue'"/> + + <!-- This macro allows us to construct a property name which contains a property expansion --> + <macrodef name="set-address-property"> + <attribute name="name"/> + <attribute name="syntax"/> + <sequential> + <property name="@{name}" value="${@{name}.@{syntax}}"/> + </sequential> + </macrodef> + + <set-address-property name="qpid.hello.topic.dest.address" syntax="${qpid.dest_syntax}"/> + <set-address-property name="qpid.goodbye.topic.dest.address" syntax="${qpid.dest_syntax}"/> + <set-address-property name="qpid.hellogoodbye.topic.dest.address" syntax="${qpid.dest_syntax}"/> + <set-address-property name="qpid.hello.queue.dest.address" syntax="${qpid.dest_syntax}"/> + <set-address-property name="qpid.goodbye.queue.dest.address" syntax="${qpid.dest_syntax}"/> + <set-address-property name="qpid.responder.queue.dest.address" syntax="${qpid.dest_syntax}"/> +</project> diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidRequestResponseClient.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidRequestResponseClient.java new file mode 100644 index 0000000000..734df1c0f3 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidRequestResponseClient.java @@ -0,0 +1,159 @@ +/* + * + * 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.qpid.jca.example.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +import org.apache.qpid.jca.example.ejb.QpidUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QpidRequestResponseClient implements MessageListener, Runnable +{ + private static final Logger _log = LoggerFactory.getLogger(QpidRequestResponseClient.class); + + private static final String DEFAULT_CF_JNDI = "QpidConnectionFactory"; + private static final String DEFAULT_DESTINATION_JNDI = "QpidResponderQueue"; + private static final String DEFAULT_MESSAGE = "Hello, World!"; + private static final int DEFAULT_MESSAGE_COUNT = 1; + private static final int DEFAULT_THREAD_COUNT = 1; + private static CountDownLatch THREAD_LATCH; + private static InitialContext CONTEXT; + + private ConnectionFactory _connectionFactory; + private Connection _connection; + private Session _session; + private CountDownLatch _latch = null; + private int _count = DEFAULT_MESSAGE_COUNT; + + /** + * @param args + */ + public static void main(String[] args) throws Exception + { + int threadCount = (System.getProperty("thread.count") == null) + ? DEFAULT_THREAD_COUNT : Integer.valueOf(System.getProperty("thread.count")); + + _log.debug("Creating " + threadCount + " threads for execution."); + + THREAD_LATCH = new CountDownLatch(threadCount); + + CONTEXT = new InitialContext(); + + for(int i = 0; i < threadCount; i++) + { + new Thread(new QpidRequestResponseClient()).start(); + } + + _log.debug("Waiting for " + threadCount + " to finish."); + THREAD_LATCH.await(10, TimeUnit.SECONDS); + + QpidUtil.closeResources(CONTEXT); + } + + @Override + public void onMessage(Message message) + { + _latch.countDown(); + + if(message instanceof TextMessage) + { + try + { + _log.debug("Thread " + Thread.currentThread().getId() + " received response message with content " + ((TextMessage)message).getText()); + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + } + + if(_latch.getCount() == _count) + { + QpidUtil.closeResources(_session, _connection); + } + + THREAD_LATCH.countDown(); + + } + + public void run() + { + MessageProducer producer = null; + Destination requestQueue = null; + Destination responseQueue = null; + + String cfName = (System.getProperty("qpid.cf.name") == null) ? DEFAULT_CF_JNDI : System.getProperty("qpid.cf.name"); + String destName = (System.getProperty("qpid.dest.name") == null) ? DEFAULT_DESTINATION_JNDI : System.getProperty("qpid.dest.name"); + + try + { + _count = (System.getProperty("message.count") == null) ? DEFAULT_MESSAGE_COUNT : Integer.valueOf(System.getProperty("message.count")); + _latch = new CountDownLatch(_count); + + _connectionFactory = (ConnectionFactory)QpidTestUtil.getFromJNDI(CONTEXT, cfName); + requestQueue = (Destination)QpidTestUtil.getFromJNDI(CONTEXT, destName); + _connection = _connectionFactory.createConnection(); + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = _session.createProducer(requestQueue); + responseQueue = _session.createTemporaryQueue(); + _session.createConsumer(responseQueue).setMessageListener(this); + + + _connection.start(); + + String content = (System.getProperty("qpid.message") == null) ? DEFAULT_MESSAGE : System.getProperty("qpid.message"); + + for(int i = 0; i < _count; i++) + { + TextMessage message = _session.createTextMessage(); + message.setText(content); + message.setJMSReplyTo(responseQueue); + producer.send(message); + + } + + _latch.await(); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(producer); + } + + } + +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestClient.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestClient.java new file mode 100644 index 0000000000..a5a33e36ec --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestClient.java @@ -0,0 +1,135 @@ +/* + * + * 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.qpid.jca.example.client; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.apache.qpid.jca.example.ejb.QpidTest; +import org.apache.qpid.jca.example.ejb.QpidUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QpidTestClient +{ + private static final Logger _log = LoggerFactory.getLogger(QpidTestClient.class); + + private static final String DEFAULT_EJB_JNDI = "QpidTestBean/remote"; + private static final String DEFAULT_CF_JNDI = "QpidConnectionFactory"; + private static final String DEFAULT_MESSAGE = "Hello,World!"; + private static final int DEFAULT_MESSAGE_COUNT = 1; + private static final boolean DEFAULT_USE_TOPIC = false; + private static final boolean DEFAULT_USE_EJB = true; + private static final String DEFAULT_DESTINATION_JNDI = "HelloQueue"; + private static final boolean DEFAULT_SAY_GOODBYE = false; + + public static void main(String[] args) throws Exception + { + String content = (System.getProperty("qpid.message") == null) ? DEFAULT_MESSAGE : System.getProperty("qpid.message"); + boolean useEJB = (System.getProperty("use.ejb") == null) ? DEFAULT_USE_EJB : Boolean.valueOf(System.getProperty("use.ejb")); + int total = (System.getProperty("message.count") == null) ? DEFAULT_MESSAGE_COUNT : Integer.valueOf(System.getProperty("message.count")); + boolean useTopic = (System.getProperty("use.topic") == null) ? DEFAULT_USE_TOPIC : Boolean.valueOf(System.getProperty("use.topic")); + String destType = (useTopic) ? "Topic" : "Queue"; + boolean goodbye = (System.getProperty("say.goodbye") == null) ? DEFAULT_SAY_GOODBYE : Boolean.valueOf(System.getProperty("say.goodbye")); + + _log.debug("Environment: "); + _log.debug("JNDI IntialContectFactory: " + System.getProperty("java.naming.factory.initial")); + _log.debug("JNDI Provider: " + System.getProperty("java.naming.provider.url")); + _log.debug("Message content: " + content); + _log.debug("Message count:" + total); + _log.debug("Protocol: " + ((useEJB) ? "EJB" : "JMS")); + _log.debug("Destination Type: " + destType); + _log.debug("Say GoodBye : " + goodbye); + + Context context = new InitialContext(); + + if(useEJB) + { + + String ejbName = (System.getProperty("qpid.ejb.name") == null) ? DEFAULT_EJB_JNDI : System.getProperty("qpid.ejb.name"); + + QpidTest ejb = (QpidTest)QpidTestUtil.getFromJNDI(context, ejbName); + + _log.debug("Found SLSB " + ejbName + "in JNDI"); + ejb.testQpidAdapter(content, total, useTopic, false, goodbye); + + } + else + { + ConnectionFactory connectionFactory = null; + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + Destination destination = null; + int count = 0; + + String cfName = (System.getProperty("qpid.cf.name") == null) ? DEFAULT_CF_JNDI : System.getProperty("qpid.cf.name"); + String destName = (System.getProperty("qpid.dest.name") == null) ? DEFAULT_DESTINATION_JNDI : System.getProperty("qpid.dest.name"); + + _log.debug("Using JMS with CF name " + cfName + " and Destination name " + destName + " to send " + total + " message(s) with content " + content); + + try + { + _log.debug("Using JNDI at " + System.getProperty("java.naming.provider.url")); + + connectionFactory = (ConnectionFactory)QpidTestUtil.getFromJNDI(context, cfName); + destination = (Destination)QpidTestUtil.getFromJNDI(context, destName); + + _log.debug("Using CF: " + connectionFactory); + _log.debug("Destination " + destination); + + connection = connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = session.createProducer(destination); + + _log.debug("Sending " + total + " message(s) with content: " + content + " to destination " + destName); + + for(int i = 0; i < total; i++) + { + TextMessage message = session.createTextMessage(content); + message.setBooleanProperty("say.goodbye", goodbye); + messageProducer.send(message); + count++; + } + + + } + catch(Exception e) + { + e.printStackTrace(); + _log.error(e.getMessage()); + } + finally + { + QpidUtil.closeResources(session, connection, context); + } + + _log.debug(count + " message(s) sent successfully"); + } + + } +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestUtil.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestUtil.java new file mode 100644 index 0000000000..7a53335d79 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/client/QpidTestUtil.java @@ -0,0 +1,52 @@ +/* + * + * 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.qpid.jca.example.client; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.spi.NamingManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QpidTestUtil { + private static final Logger _log = LoggerFactory.getLogger(QpidTestUtil.class); + + /* + * Encapsulate looking up in JNDI and working around a seeming bug in OpenEJB which returns a + * Reference when it should just return an object constructed from it + */ + static Object getFromJNDI(Context context, String name) throws NamingException, Exception + { + Object o = context.lookup(name); + if (o instanceof Reference) + { + _log.debug("Got a Reference back from JNDI for " + name + " - working around"); + return NamingManager.getObjectInstance(o, null, null, null); + } + else + { + return o; + } + } + +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeListenerBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeListenerBean.java new file mode 100644 index 0000000000..9cf220de2a --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeListenerBean.java @@ -0,0 +1,65 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import java.util.Date; + +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), + @ActivationConfigProperty(propertyName = "destination", propertyValue = "@qpid.goodbye.queue.jndi.name@"), + @ActivationConfigProperty(propertyName = "connectionURL", propertyValue = "@broker.url@"), + @ActivationConfigProperty(propertyName = "useLocalTx", propertyValue = "false"), + @ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10") +}) +public class QpidGoodByeListenerBean implements MessageListener +{ + private static final Logger _log = LoggerFactory.getLogger(QpidGoodByeListenerBean.class); + + @Override + public void onMessage(Message message) + { + try + { + if(message instanceof TextMessage) + { + String content = ((TextMessage)message).getText(); + + _log.info("Received text message with contents: [" + content + "] at " + new Date()); + } + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + + } + +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeSubscriberBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeSubscriberBean.java new file mode 100644 index 0000000000..8ad8aaa482 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidGoodByeSubscriberBean.java @@ -0,0 +1,27 @@ +package org.apache.qpid.jca.example.ejb; + +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Message; +import javax.jms.MessageListener; + +@MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), + @ActivationConfigProperty(propertyName = "destination", propertyValue = "@qpid.goodbye.topic.jndi.name@"), + @ActivationConfigProperty(propertyName = "connectionURL", propertyValue = "@broker.url@"), + @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "NotDurable"), + @ActivationConfigProperty(propertyName = "subscriptionName", propertyValue = "hello.Topic"), + @ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10") +}) + +public class QpidGoodByeSubscriberBean implements MessageListener +{ + + @Override + public void onMessage(Message message) + { + System.out.println(message); + } + +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloListenerBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloListenerBean.java new file mode 100644 index 0000000000..d6d08d1557 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloListenerBean.java @@ -0,0 +1,118 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import java.util.Date; + +import javax.annotation.PostConstruct; +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), + @ActivationConfigProperty(propertyName = "destination", propertyValue = "@qpid.hello.queue.jndi.name@"), + @ActivationConfigProperty(propertyName = "connectionURL", propertyValue = "@broker.url@"), + @ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10") +}) +public class QpidHelloListenerBean implements MessageListener +{ + private static final Logger _log = LoggerFactory.getLogger(QpidHelloListenerBean.class); + + private ConnectionFactory _connectionFactory; + + private Destination _queue; + + @PostConstruct + public void init() + { + InitialContext context = null; + + try + { + context = new InitialContext(); + _connectionFactory = (ConnectionFactory)context.lookup("java:comp/env/QpidJMSXA"); + _queue = (Destination)context.lookup("@qpid.goodbye.queue.jndi.name@"); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(context); + } + + } + + @Override + public void onMessage(Message message) + { + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + TextMessage response = null; + + try + { + if(message instanceof TextMessage) + { + String content = ((TextMessage)message).getText(); + + _log.info("Received text message with contents: [" + content + "] at " + new Date()); + + StringBuffer temp = new StringBuffer(); + temp.append("QpidHelloListenerBean received message with content: [" + content); + temp.append("] at " + new Date()); + + if(message.propertyExists("say.goodbye") && message.getBooleanProperty("say.goodbye")) + { + connection = _connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = session.createProducer(_queue); + response = session.createTextMessage(temp.toString()); + messageProducer.send(response); + } + } + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(session, connection); + } + } +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloSubscriberBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloSubscriberBean.java new file mode 100644 index 0000000000..43ccf9defd --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidHelloSubscriberBean.java @@ -0,0 +1,121 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import java.util.Date; + +import javax.annotation.PostConstruct; +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +@MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), + @ActivationConfigProperty(propertyName = "destination", propertyValue = "@qpid.hello.topic.jndi.name@"), + @ActivationConfigProperty(propertyName = "connectionURL", propertyValue = "@broker.url@"), + @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "NotDurable"), + @ActivationConfigProperty(propertyName = "subscriptionName", propertyValue = "hello.Topic"), + @ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10") +}) +public class QpidHelloSubscriberBean implements MessageListener +{ + private static final Logger _log = LoggerFactory.getLogger(QpidHelloSubscriberBean.class); + + private ConnectionFactory _connectionFactory; + + private Destination _topic; + + @PostConstruct + public void init() + { + InitialContext context = null; + + try + { + context = new InitialContext(); + _connectionFactory = (ConnectionFactory)context.lookup("java:comp/env/QpidJMSXA"); + _topic = (Destination)context.lookup("@qpid.goodbye.topic.jndi.name@"); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(context); + } + + } + + @Override + public void onMessage(Message message) + { + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + TextMessage response = null; + + try + { + if(message instanceof TextMessage) + { + String content = ((TextMessage)message).getText(); + + _log.info("Received text message with contents: [" + content + "] at " + new Date()); + + StringBuffer temp = new StringBuffer(); + temp.append("QpidHelloSubscriberBean received message with content: [" + content); + temp.append("] at " + new Date()); + + if(message.propertyExists("say.goodbye") && message.getBooleanProperty("say.goodbye")) + { + connection = _connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = session.createProducer(_topic); + response = session.createTextMessage(temp.toString()); + messageProducer.send(response); + } + } + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(session, connection); + } + } +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidJMSResponderBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidJMSResponderBean.java new file mode 100644 index 0000000000..74d6fb6d89 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidJMSResponderBean.java @@ -0,0 +1,100 @@ +package org.apache.qpid.jca.example.ejb; + +import java.util.Date; + +import javax.annotation.PostConstruct; +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@MessageDriven(activationConfig = { + @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), + @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), + @ActivationConfigProperty(propertyName = "destination", propertyValue = "@qpid.responder.queue.jndi.name@"), + @ActivationConfigProperty(propertyName = "connectionURL", propertyValue = "@broker.url@"), + @ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10") +}) +public class QpidJMSResponderBean implements MessageListener +{ + + private static final Logger _log = LoggerFactory.getLogger(QpidJMSResponderBean.class); + + private ConnectionFactory _connectionFactory; + + @PostConstruct + public void init() + { + InitialContext context = null; + + try + { + context = new InitialContext(); + _connectionFactory = (ConnectionFactory)context.lookup("java:comp/env/QpidJMSXA"); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(context); + } + + } + + @Override + public void onMessage(Message message) + { + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + TextMessage response = null; + + try + { + if(message instanceof TextMessage) + { + String content = ((TextMessage)message).getText(); + + _log.info("Received text message with contents: [" + content + "] at " + new Date()); + + StringBuffer temp = new StringBuffer(); + temp.append("QpidJMSResponderBean received message with content: [" + content); + temp.append("] at " + new Date()); + + if(message.getJMSReplyTo() != null) + { + connection = _connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = session.createProducer(message.getJMSReplyTo()); + response = session.createTextMessage(); + response.setText(temp.toString()); + messageProducer.send(response); + } + else + { + _log.warn("Response was requested with no JMSReplyToDestination set. Will not respond to message."); + } + } + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(session, connection); + } + } +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTest.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTest.java new file mode 100644 index 0000000000..14488fda53 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTest.java @@ -0,0 +1,30 @@ +/* + * + * 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.qpid.jca.example.ejb; + +public interface QpidTest +{ + public void testQpidAdapter(String content) throws Exception; + public void testQpidAdapter(String content, int count) throws Exception; + public void testQpidAdapter(String content, int count, boolean useTopic) throws Exception; + public void testQpidAdapter(String content, int count, boolean useTopic, boolean respond, boolean sayGoodbye) throws Exception; +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestBean.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestBean.java new file mode 100644 index 0000000000..17e37b9475 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestBean.java @@ -0,0 +1,123 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import javax.annotation.PostConstruct; +import javax.ejb.Stateless; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Stateless +public class QpidTestBean implements QpidTestRemote, QpidTestLocal +{ + + private static final Logger _log = LoggerFactory.getLogger(QpidTestBean.class); + + private ConnectionFactory _connectionFactory; + + private Destination _queue; + + private Destination _topic; + + @PostConstruct + public void init() + { + InitialContext context = null; + + try + { + context = new InitialContext(); + _connectionFactory = (ConnectionFactory)context.lookup("java:comp/env/QpidJMSXA"); + _queue = (Destination)context.lookup("@qpid.hello.queue.jndi.name@"); + _topic = (Destination)context.lookup("@qpid.hello.topic.jndi.name@"); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(context); + } + + } + @Override + public void testQpidAdapter(String content) throws Exception + { + testQpidAdapter(content, 1); + } + + @Override + public void testQpidAdapter(String content, int count) throws Exception + { + testQpidAdapter(content, count, false); + } + + public void testQpidAdapter(final String content, int count, boolean useTopic) throws Exception + { + testQpidAdapter(content, count, useTopic, false, false); + } + + @Override + public void testQpidAdapter(String content, int count, boolean useTopic, + boolean respond, boolean sayGoodbye) throws Exception + { + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + + _log.info("Sending " + count + " message(s) to MDB with content " + content); + + try + { + connection = _connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = (useTopic) ? session.createProducer(_topic) : session.createProducer(_queue); + + for(int i = 0; i < count; i++) + { + TextMessage message = session.createTextMessage(content); + message.setBooleanProperty("say.goodbye", sayGoodbye); + messageProducer.send(message); + } + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + throw e; + } + finally + { + QpidUtil.closeResources(messageProducer, session, connection); + } + } + +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestLocal.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestLocal.java new file mode 100644 index 0000000000..73a0de08c2 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestLocal.java @@ -0,0 +1,29 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import javax.ejb.Local; + +@Local +public interface QpidTestLocal extends QpidTest +{ +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestRemote.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestRemote.java new file mode 100644 index 0000000000..2abb4d71f5 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidTestRemote.java @@ -0,0 +1,29 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import javax.ejb.Remote; + +@Remote +public interface QpidTestRemote extends QpidTest +{ +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidUtil.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidUtil.java new file mode 100644 index 0000000000..d96a4e8163 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/ejb/QpidUtil.java @@ -0,0 +1,90 @@ +/* + * + * 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.qpid.jca.example.ejb; + +import java.lang.reflect.Method; +import java.util.Enumeration; + +import javax.jms.Message; +import javax.jms.TextMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QpidUtil +{ + private static final Logger _log = LoggerFactory.getLogger(QpidTestBean.class); + + public static void handleMessage(String beanName, final Message message) throws Exception + { + if(message instanceof TextMessage) + { + String content = ((TextMessage)message).getText(); + _log.debug(beanName + ": Received text message with contents " + content); + + if(content.contains("PrintEnv")) + { + printJMSHeaders(message); + printProperties(message); + } + } + } + + @SuppressWarnings("rawtypes") + public static void printProperties(final Message message) throws Exception + { + _log.debug("Priting Message Properties:"); + + Enumeration e = message.getPropertyNames(); + + while(e.hasMoreElements()) + { + _log.debug(e + ":" + message.getObjectProperty(e.toString())); + } + } + + public static void printJMSHeaders(final Message message) throws Exception + { + _log.debug("JMSCorrelationID:" + message.getJMSCorrelationID()); + _log.debug("JMSDeliveryMode:" + message.getJMSDeliveryMode()); + _log.debug("JMSExpires:" + message.getJMSExpiration()); + _log.debug("JMSMessageID:" + message.getJMSMessageID()); + _log.debug("JMSPriority:" + message.getJMSPriority()); + _log.debug("JMSTimestamp:" + message.getJMSTimestamp()); + _log.debug("JMSType:" + message.getJMSType()); + _log.debug("JMSReplyTo:" + message.getJMSReplyTo()); + } + + public static void closeResources(Object...objects) + { + try + { + for(Object object: objects) + { + Method close = object.getClass().getMethod("close", new Class[]{}); + close.invoke(object, new Object[]{}); + } + } + catch(Exception ignore) + { + } + } +} diff --git a/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/web/QpidTestServlet.java b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/web/QpidTestServlet.java new file mode 100644 index 0000000000..71289b22c3 --- /dev/null +++ b/qpid/java/jca/example/src/main/java/org/apache/qpid/jca/example/web/QpidTestServlet.java @@ -0,0 +1,209 @@ +/* + * + * 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.qpid.jca.example.web; +import java.io.IOException; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.InitialContext; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.transaction.UserTransaction; + +import org.apache.qpid.jca.example.ejb.QpidTest; +import org.apache.qpid.jca.example.ejb.QpidUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("serial") +public class QpidTestServlet extends HttpServlet +{ + private static final Logger _log = LoggerFactory.getLogger(QpidTestServlet.class); + + private static final String DEFAULT_MESSAGE = "Hello, World!"; + private static final int DEFAULT_COUNT = 1; + private static final boolean DEFAULT_TOPIC = false; + private static final boolean DEFAULT_XA = false; + private static final boolean DEFAULT_SAY_GOODBYE = true; + + private ConnectionFactory _connectionFactory; + + private Destination _queue; + + private Destination _topic; + + public void init(ServletConfig config) throws ServletException + { + + InitialContext context = null; + + try + { + context = new InitialContext(); + _connectionFactory = (ConnectionFactory)context.lookup("java:comp/env/QpidJMSXA"); + _queue = (Destination)context.lookup("@qpid.hello.queue.jndi.name@"); + _topic = (Destination)context.lookup("@qpid.hello.topic.jndi.name@"); + + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + throw new ServletException(e.getMessage(), e); + } + finally + { + QpidUtil.closeResources(context); + } + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + doPost(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + InitialContext ctx = null; + Connection connection = null; + Session session = null; + MessageProducer messageProducer = null; + UserTransaction ut = null; + boolean useXA = false; + boolean rollback = false; + + try + { + String content = (req.getParameter("message") == null) ? DEFAULT_MESSAGE : req.getParameter("message"); + boolean useEJB = (req.getParameter("useEJB") == null) ? false : Boolean.valueOf(req.getParameter("useEJB")); + int count = (req.getParameter("count") == null) ? DEFAULT_COUNT : Integer.valueOf(req.getParameter("count")); + boolean useTopic = (req.getParameter("useTopic") == null) ? DEFAULT_TOPIC : Boolean.valueOf(req.getParameter("useTopic")); + useXA = (req.getParameter("useXA") == null) ? DEFAULT_XA : Boolean.valueOf(req.getParameter("useXA")); + ctx = new InitialContext(); + boolean sayGoodBye = (req.getParameter("sayGoodBye") == null) ? DEFAULT_SAY_GOODBYE : Boolean.valueOf(req.getParameter("sayGoodBye")); + + _log.debug("Environment: "); + _log.debug("Message content: " + content); + _log.debug("Message count:" + count); + _log.debug("Protocol: " + ((useEJB) ? "EJB" : "JMS")); + _log.debug("Destination Type: " + ((useTopic) ? "Topic" : "Queue")); + _log.debug("Using XA: " + useXA); + _log.debug("Say GoodBye: ", sayGoodBye); + + resp.getOutputStream().println("Environment: "); + resp.getOutputStream().println("Message content: " + content); + resp.getOutputStream().println("Message count:" + count); + resp.getOutputStream().println("Protocol: " + ((useEJB) ? "EJB" : "JMS")); + resp.getOutputStream().println("Destination Type: " + ((useTopic) ? "Topic" : "Queue")); + resp.getOutputStream().println("Using XA: " + useXA); + resp.getOutputStream().println("Say GoodBye: " + sayGoodBye); + + if(useEJB) + { + QpidTest ejb = (QpidTest)ctx.lookup("java:comp/env/QpidTestBean"); + ejb.testQpidAdapter(content, count, useTopic, false, sayGoodBye); + } + else + { + if(useXA) + { + ut = (UserTransaction)ctx.lookup("java:comp/UserTransaction"); + ut.begin(); + } + + connection = _connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + messageProducer = (useTopic) ? session.createProducer(_topic) : session.createProducer(_queue); + + for(int i = 0; i < count; i++) + { + TextMessage message = session.createTextMessage(content); + message.setBooleanProperty("say.goodbye", sayGoodBye); + messageProducer.send(message); + } + + } + + resp.getOutputStream().println("Sent " + count + " messages with content '" + content + "'"); + resp.getOutputStream().flush(); + + } + catch(Exception e) + { + + if(useXA && ut != null) + { + try + { + rollback = true; + ut.setRollbackOnly(); + } + catch(Exception ex) + { + _log.error(ex.getMessage(), ex); + throw new ServletException(ex.getMessage(), ex); + } + } + + _log.error(e.getMessage(), e); + throw new ServletException(e.getMessage(), e); + } + finally + { + if(useXA && ut != null) + { + try + { + if(rollback) + { + ut.rollback(); + } + else + { + ut.commit(); + } + } + catch(Exception e) + { + _log.error(e.getMessage(), e); + throw new ServletException(e.getMessage(), e); + + } + } + + QpidUtil.closeResources(session, connection, ctx); + } + } + + + +} + + diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryObjectFactory.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryObjectFactory.java new file mode 100644 index 0000000000..2dc94ed194 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryObjectFactory.java @@ -0,0 +1,62 @@ +/* + * + * 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.qpid.ra; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameNotFoundException; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; + +/** + * + * A ConnectionFactoryObjectFactory. + * + * Given a reference - reconstructs a QpidRAConnectionFactory + * + */ +public class ConnectionFactoryObjectFactory implements ObjectFactory +{ + static final String QPID_CF = "QPID-CF"; + + public Object getObjectInstance(final Object ref, final Name name, final Context ctx, final Hashtable<?,?> props) throws Exception + { + if (!(ref instanceof Reference)) + { + throw new IllegalArgumentException(); + } + + RefAddr ra = ((Reference)ref).get(QPID_CF); + if (ra == null) + { + throw new NameNotFoundException(); + } + + byte[] bytes = (byte[])ra.getContent(); + + return Util.deserialize(bytes); + + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryProperties.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryProperties.java new file mode 100644 index 0000000000..be129a67cc --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/ConnectionFactoryProperties.java @@ -0,0 +1,215 @@ +/* + * + * 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.qpid.ra; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + */ +public class ConnectionFactoryProperties +{ + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(ConnectionFactoryProperties.class); + + private boolean _hasBeenUpdated = false; + + private String _clientID; + + private String _connectionURL; + + private String _userName; + + private String _password; + + private String _host; + + private Integer _port; + + private String _path; + + private Boolean _localTx = Boolean.FALSE; + + public String getClientId() + { + if (_log.isTraceEnabled()) + { + _log.trace("getClientID()"); + } + return _clientID; + } + + public void setClientId(final String clientID) + { + if (_log.isTraceEnabled()) + { + _log.trace("setClientID(" + clientID + ")"); + } + _hasBeenUpdated = true; + this._clientID = clientID; + } + + public boolean isHasBeenUpdated() + { + return _hasBeenUpdated; + } + + public String getConnectionURL() + { + if (_log.isTraceEnabled()) + { + _log.trace("getConnectionURL()"); + } + return _connectionURL; + } + + public void setConnectionURL(final String connectionURL) + { + if (_log.isTraceEnabled()) + { + _log.trace("setConnectionURL(" + connectionURL + ")"); + } + _hasBeenUpdated = true; + this._connectionURL = connectionURL; + } + + public String getPassword() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDefaultPassword()"); + } + return _password; + } + + public void setPassword(final String defaultPassword) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDefaultPassword(" + defaultPassword + ")"); + } + _hasBeenUpdated = true; + this._password = defaultPassword; + } + + public String getUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDefaultUsername()"); + } + return _userName; + } + + public void setUserName(final String defaultUsername) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDefaultUsername(" + defaultUsername + ")"); + } + _hasBeenUpdated = true; + this._userName = defaultUsername; + } + + public String getHost() + { + if (_log.isTraceEnabled()) + { + _log.trace("getHost()"); + } + return _host; + } + + public void setHost(final String host) + { + if (_log.isTraceEnabled()) + { + _log.trace("setHost(" + host + ")"); + } + _hasBeenUpdated = true; + this._host = host; + } + + public Integer getPort() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPort()"); + } + return _port; + } + + public void setPort(final Integer port) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPort(" + port + ")"); + } + _hasBeenUpdated = true; + this._port = port; + } + + public String getPath() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPath()"); + } + return _path; + } + + public void setPath(final String path) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPath(" + path + ")"); + } + _hasBeenUpdated = true; + this._path = path; + } + + public Boolean isUseLocalTx() + { + if (_log.isTraceEnabled()) + { + _log.trace("isUseLocalTx()"); + } + return _localTx; + } + + public void setUseLocalTx(Boolean localTx) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseLocalTx(" + localTx + ")"); + } + + if(localTx != null) + { + _hasBeenUpdated = true; + this._localTx = localTx; + } + + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRABytesMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRABytesMessage.java new file mode 100644 index 0000000000..d30a45c739 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRABytesMessage.java @@ -0,0 +1,462 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + * + */ +public class QpidRABytesMessage extends QpidRAMessage implements BytesMessage +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRABytesMessage.class); + + /** + * Create a new wrapper + * @param message the message + * @param session the session + */ + public QpidRABytesMessage(final BytesMessage message, final QpidRASessionImpl session) + { + super(message, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(message) + ", " + session + ")"); + } + } + + /** + * Get body length + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long getBodyLength() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getBodyLength()"); + } + + return ((BytesMessage)_message).getBodyLength(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean readBoolean() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readBoolean()"); + } + + return ((BytesMessage)_message).readBoolean(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte readByte() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readByte()"); + } + + return ((BytesMessage)_message).readByte(); + } + + /** + * Read + * @param value The value + * @param length The length + * @return The result + * @exception JMSException Thrown if an error occurs + */ + public int readBytes(final byte[] value, final int length) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readBytes(" + value + ", " + length + ")"); + } + + return ((BytesMessage)_message).readBytes(value, length); + } + + /** + * Read + * @param value The value + * @return The result + * @exception JMSException Thrown if an error occurs + */ + public int readBytes(final byte[] value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readBytes(" + value + ")"); + } + + return ((BytesMessage)_message).readBytes(value); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public char readChar() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readChar()"); + } + + return ((BytesMessage)_message).readChar(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public double readDouble() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readDouble()"); + } + + return ((BytesMessage)_message).readDouble(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public float readFloat() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readFloat()"); + } + + return ((BytesMessage)_message).readFloat(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int readInt() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readInt()"); + } + + return ((BytesMessage)_message).readInt(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long readLong() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readLong()"); + } + + return ((BytesMessage)_message).readLong(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public short readShort() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readShort()"); + } + + return ((BytesMessage)_message).readShort(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int readUnsignedByte() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readUnsignedByte()"); + } + + return ((BytesMessage)_message).readUnsignedByte(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int readUnsignedShort() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readUnsignedShort()"); + } + + return ((BytesMessage)_message).readUnsignedShort(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String readUTF() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readUTF()"); + } + + return ((BytesMessage)_message).readUTF(); + } + + /** + * Reset + * @exception JMSException Thrown if an error occurs + */ + public void reset() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("reset()"); + } + + ((BytesMessage)_message).reset(); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeBoolean(final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBoolean(" + value + ")"); + } + + ((BytesMessage)_message).writeBoolean(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeByte(final byte value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeByte(" + value + ")"); + } + + ((BytesMessage)_message).writeByte(value); + } + + /** + * Write + * @param value The value + * @param offset The offset + * @param length The length + * @exception JMSException Thrown if an error occurs + */ + public void writeBytes(final byte[] value, final int offset, final int length) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBytes(" + value + ", " + offset + ", " + length + ")"); + } + + ((BytesMessage)_message).writeBytes(value, offset, length); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeBytes(final byte[] value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBytes(" + value + ")"); + } + + ((BytesMessage)_message).writeBytes(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeChar(final char value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeChar(" + value + ")"); + } + + ((BytesMessage)_message).writeChar(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeDouble(final double value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeDouble(" + value + ")"); + } + + ((BytesMessage)_message).writeDouble(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeFloat(final float value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeFloat(" + value + ")"); + } + + ((BytesMessage)_message).writeFloat(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeInt(final int value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeInt(" + value + ")"); + } + + ((BytesMessage)_message).writeInt(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeLong(final long value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeLong(" + value + ")"); + } + + ((BytesMessage)_message).writeLong(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeObject(final Object value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeObject(" + Util.asString(value) + ")"); + } + + ((BytesMessage)_message).writeObject(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeShort(final short value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeShort(" + value + ")"); + } + + ((BytesMessage)_message).writeShort(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeUTF(final String value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeUTF(" + value + ")"); + } + + ((BytesMessage)_message).writeUTF(value); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactory.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactory.java new file mode 100644 index 0000000000..1e8fb13c79 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactory.java @@ -0,0 +1,58 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; + +import javax.jms.ConnectionFactory; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnectionFactory; +import javax.jms.XAConnectionFactory; +import javax.jms.XAQueueConnectionFactory; +import javax.jms.XATopicConnectionFactory; +import javax.resource.Referenceable; + +/** + * An aggregate interface for the JMS connection factories + * + */ +public interface QpidRAConnectionFactory extends ConnectionFactory, TopicConnectionFactory, QueueConnectionFactory, + XAConnectionFactory, XAQueueConnectionFactory, XATopicConnectionFactory, Serializable, Referenceable +{ + /** Connection factory capable of handling connections */ + public static final int CONNECTION = 0; + + /** Connection factory capable of handling queues */ + public static final int QUEUE_CONNECTION = 1; + + /** Connection factory capable of handling topics */ + public static final int TOPIC_CONNECTION = 2; + + /** Connection factory capable of handling XA connections */ + public static final int XA_CONNECTION = 3; + + /** Connection factory capable of handling XA queues */ + public static final int XA_QUEUE_CONNECTION = 4; + + /** Connection factory capable of handling XA topics */ + public static final int XA_TOPIC_CONNECTION = 5; +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactoryImpl.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactoryImpl.java new file mode 100644 index 0000000000..77a38d5b34 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionFactoryImpl.java @@ -0,0 +1,442 @@ +/* + * + * 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.qpid.ra; + +import java.io.IOException; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.TopicConnection; +import javax.jms.XAConnection; +import javax.jms.XAQueueConnection; +import javax.jms.XATopicConnection; +import javax.naming.BinaryRefAddr; +import javax.naming.Reference; +import javax.resource.spi.ConnectionManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The connection factory + * + */ +public class QpidRAConnectionFactoryImpl implements QpidRAConnectionFactory +{ + /** Serial version UID */ + private static final long serialVersionUID = -5306006173783505760L; + + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAConnectionFactoryImpl.class); + + /** The managed connection factory */ + private final QpidRAManagedConnectionFactory _mcf; + + /** The connection manager */ + private ConnectionManager _cm; + + /** Naming reference */ + private Reference _reference; + + /** + * Constructor + * @param mcf The managed connection factory + * @param cm The connection manager + */ + public QpidRAConnectionFactoryImpl(final QpidRAManagedConnectionFactory mcf, final ConnectionManager cm) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mcf + ", " + cm + ")"); + } + + this._mcf = mcf; + + if (cm == null) + { + // This is standalone usage, no appserver + this._cm = new QpidRAConnectionManager(); + if (_log.isTraceEnabled()) + { + _log.trace("Created new ConnectionManager=" + this._cm); + } + } + else + { + this._cm = cm; + } + + if (_log.isTraceEnabled()) + { + _log.trace("Using ManagedConnectionFactory=" + mcf + ", ConnectionManager=" + this._cm); + } + } + + /** + * Set the reference + * @param reference The reference + */ + public void setReference(final Reference reference) + { + if (_log.isTraceEnabled()) + { + _log.trace("setReference(" + reference + ")"); + } + + this._reference = reference; + } + + /** + * Get the reference + * @return The reference + */ + public Reference getReference() + { + if (_log.isTraceEnabled()) + { + _log.trace("getReference()"); + } + if (_reference == null) + { + try + { + _reference = new Reference(this.getClass().getCanonicalName(), + new BinaryRefAddr(ConnectionFactoryObjectFactory.QPID_CF, + Util.serialize(this)), + ConnectionFactoryObjectFactory.class.getCanonicalName(), + null); + } + catch (final IOException ioe) + { + _log.error("Error while giving object Reference.", ioe); + } + } + + return _reference; + + } + + /** + * Create a queue connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public QueueConnection createQueueConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createQueueConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.QUEUE_CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created queue connection: " + s); + } + + return s; + } + + /** + * Create a queue connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public QueueConnection createQueueConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createQueueConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.QUEUE_CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created queue connection: " + s); + } + + return s; + } + + /** + * Create a topic connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public TopicConnection createTopicConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createTopicConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.TOPIC_CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created topic connection: " + s); + } + + return s; + } + + /** + * Create a topic connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public TopicConnection createTopicConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createTopicConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.TOPIC_CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created topic connection: " + s); + } + + return s; + } + + /** + * Create a connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public Connection createConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, _cm, QpidRAConnectionFactory.CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created connection: " + s); + } + + return s; + } + + /** + * Create a connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public Connection createConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, _cm, QpidRAConnectionFactory.CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created connection: " + s); + } + + return s; + } + + /** + * Create a XA queue connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XAQueueConnection createXAQueueConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXAQueueConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.XA_QUEUE_CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA queue connection: " + s); + } + + return s; + } + + /** + * Create a XA queue connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XAQueueConnection createXAQueueConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXAQueueConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.XA_QUEUE_CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA queue connection: " + s); + } + + return s; + } + + /** + * Create a XA topic connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XATopicConnection createXATopicConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXATopicConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.XA_TOPIC_CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA topic connection: " + s); + } + + return s; + } + + /** + * Create a XA topic connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XATopicConnection createXATopicConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXATopicConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, + _cm, + QpidRAConnectionFactory.XA_TOPIC_CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA topic connection: " + s); + } + + return s; + } + + /** + * Create a XA connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XAConnection createXAConnection() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXAConnection()"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, _cm, QpidRAConnectionFactory.XA_CONNECTION); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA connection: " + s); + } + + return s; + } + + /** + * Create a XA connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public XAConnection createXAConnection(final String userName, final String password) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXAConnection(" + userName + ", ****)"); + } + + QpidRASessionFactoryImpl s = new QpidRASessionFactoryImpl(_mcf, _cm, QpidRAConnectionFactory.XA_CONNECTION); + s.setUserName(userName); + s.setPassword(password); + + if (_log.isTraceEnabled()) + { + _log.trace("Created XA connection: " + s); + } + + return s; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionManager.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionManager.java new file mode 100644 index 0000000000..7ba5dd5374 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionManager.java @@ -0,0 +1,80 @@ +/* + * + * 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.qpid.ra; + +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionManager; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The connection manager used in non-managed environments. + * + */ +public class QpidRAConnectionManager implements ConnectionManager +{ + /** Serial version UID */ + private static final long serialVersionUID = 688529567919039006L; + + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAConnectionManager.class); + + /** + * Constructor + */ + public QpidRAConnectionManager() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + } + + /** + * Allocates a connection + * @param mcf The managed connection factory + * @param cxRequestInfo The connection request information + * @return The connection + * @exception ResourceException Thrown if there is a problem obtaining the connection + */ + public Object allocateConnection(final ManagedConnectionFactory mcf, final ConnectionRequestInfo cxRequestInfo) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("allocateConnection(" + mcf + ", " + cxRequestInfo + ")"); + } + + ManagedConnection mc = mcf.createManagedConnection(null, cxRequestInfo); + Object c = mc.getConnection(null, cxRequestInfo); + + if (_log.isTraceEnabled()) + { + _log.trace("Allocated connection: " + c + ", with managed connection: " + mc); + } + + return c; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionMetaData.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionMetaData.java new file mode 100644 index 0000000000..ec14c2a492 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionMetaData.java @@ -0,0 +1,208 @@ +/* + * + * 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.qpid.ra; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; + +import javax.jms.ConnectionMetaData; + +import org.apache.qpid.client.CustomJMSXProperty; +import org.apache.qpid.common.QpidProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements javax.jms.ConnectionMetaData + * + */ +public class QpidRAConnectionMetaData implements ConnectionMetaData +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAConnectionMetaData.class); + + private static final String PROVIDER_VERSION ; + private static final int PROVIDER_MAJOR ; + private static final int PROVIDER_MINOR ; + private static final String[] JMSX_PROPERTY_NAMES ; + + /** + * Constructor + */ + public QpidRAConnectionMetaData() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + } + + /** + * Get the JMS version + * @return The version + */ + public String getJMSVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSVersion()"); + } + + return "1.1"; + } + + /** + * Get the JMS major version + * @return The major version + */ + public int getJMSMajorVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSMajorVersion()"); + } + + return 1; + } + + /** + * Get the JMS minor version + * @return The minor version + */ + public int getJMSMinorVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSMinorVersion()"); + } + + return 1; + } + + /** + * Get the JMS provider name + * @return The name + */ + public String getJMSProviderName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSProviderName()"); + } + + return QpidProperties.getProductName() + " Resource Adapter" ; + } + + /** + * Get the provider version + * @return The version + */ + public String getProviderVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getProviderVersion()"); + } + + return PROVIDER_VERSION ; + } + + /** + * Get the provider major version + * @return The version + */ + public int getProviderMajorVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getProviderMajorVersion()"); + } + + return PROVIDER_MAJOR ; + } + + /** + * Get the provider minor version + * @return The version + */ + public int getProviderMinorVersion() + { + if (_log.isTraceEnabled()) + { + _log.trace("getProviderMinorVersion()"); + } + + return PROVIDER_MINOR ; + } + + /** + * Get the JMS XPropertyNames + * @return The names + */ + public Enumeration<String> getJMSXPropertyNames() + { + // Bug in CustomJMSXProperty.asEnumeration() so we handle this here + return Collections.enumeration(Arrays.asList(JMSX_PROPERTY_NAMES)) ; + } + + static + { + final String version = QpidProperties.getReleaseVersion() ; + int major = -1 ; + int minor = -1 ; + if (version != null) + { + final int separator = version.indexOf('.') ; + if (separator != -1) + { + major = parseInt(version.substring(0, separator), "major") ; + minor = parseInt(version.substring(separator+1, version.length()), "minor") ; + } + } + PROVIDER_VERSION = version ; + PROVIDER_MAJOR = major ; + PROVIDER_MINOR = minor ; + + final CustomJMSXProperty[] properties = CustomJMSXProperty.values(); + final String[] names = new String[properties.length] ; + int count = 0 ; + for(CustomJMSXProperty property : properties) + { + names[count++] = property.toString() ; + } + JMSX_PROPERTY_NAMES = names ; + } + + private static int parseInt(final String value, final String name) + { + try + { + return Integer.parseInt(value) ; + } + catch (final NumberFormatException nfe) + { + _log.warn("Failed to parse " + name + ": " + value) ; + return -1 ; + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionRequestInfo.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionRequestInfo.java new file mode 100644 index 0000000000..c37a264ebc --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAConnectionRequestInfo.java @@ -0,0 +1,361 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.Session; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionRequestInfo; + +import org.apache.qpid.jms.ConnectionURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Connection request information + * + */ +public class QpidRAConnectionRequestInfo implements ConnectionRequestInfo +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAConnectionRequestInfo.class); + + /** The user name */ + private String _userName; + + /** The password */ + private String _password; + + /** The client id */ + private String _clientID; + + /** The type */ + private final int _type; + + /** Use transactions */ + private final boolean _transacted; + + /** The acknowledge mode */ + private final int _acknowledgeMode; + + /** + * Constructor + * @param ra The resource adapter. + * @param type The connection type + * @throws ResourceException + */ + public QpidRAConnectionRequestInfo(final QpidResourceAdapter ra, final int type) + throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + ra + ")"); + } + + final QpidRAProperties properties = ra.getProperties() ; + if (properties.getConnectionURL() != null) + { + final ConnectionURL connectionURL = ra.getDefaultAMQConnectionFactory().getConnectionURL() ; + _userName = connectionURL.getUsername(); + _password = connectionURL.getPassword(); + _clientID = connectionURL.getClientName(); + } + else + { + _userName = ra.getDefaultUserName(); + _password = ra.getDefaultPassword(); + _clientID = ra.getClientId(); + } + this._type = type; + _transacted = true; + _acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + } + + /** + * Constructor + * @param type The connection type + */ + public QpidRAConnectionRequestInfo(final int type) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + type + ")"); + } + + this._type = type; + _transacted = true; + _acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + } + + /** + * Constructor + * @param transacted Use transactions + * @param acknowledgeMode The acknowledge mode + * @param type The connection type + */ + public QpidRAConnectionRequestInfo(final boolean transacted, final int acknowledgeMode, final int type) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + transacted + + ", " + + acknowledgeMode + + ", " + + type + + ")"); + } + + this._transacted = transacted; + this._acknowledgeMode = acknowledgeMode; + this._type = type; + } + + /** + * Fill in default values if they are missing + * @param connectionURL The connection URL + */ + public void setDefaults(final ConnectionURL connectionURL) + { + if (_userName == null) + { + _userName = connectionURL.getUsername(); + } + if (_password == null) + { + _password = connectionURL.getPassword(); + } + if (_clientID == null) + { + _clientID = connectionURL.getClientName(); + } + } + + /** + * Fill in default values if they are missing + * @param ra The resource adapter + * @throws ResourceException + */ + public void setDefaults(final QpidResourceAdapter ra) + throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDefaults(" + ra + ")"); + } + + final QpidRAProperties properties = ra.getProperties() ; + if (properties.getConnectionURL() != null) + { + setDefaults(ra.getDefaultAMQConnectionFactory().getConnectionURL()) ; + } + else + { + if (_userName == null) + { + _userName = ra.getDefaultUserName(); + } + if (_password == null) + { + _password = ra.getDefaultPassword(); + } + if (_clientID == null) + { + _clientID = ra.getClientId(); + } + } + } + + /** + * Get the user name + * @return The value + */ + public String getUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUserName()"); + } + + return _userName; + } + + /** + * Set the user name + * @param userName The value + */ + public void setUserName(final String userName) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUserName(" + userName + ")"); + } + + this._userName = userName; + } + + /** + * Get the password + * @return The value + */ + public String getPassword() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPassword()"); + } + + return _password; + } + + /** + * Set the password + * @param password The value + */ + public void setPassword(final String password) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPassword(****)"); + } + + this._password = password; + } + + /** + * Get the client id + * @return The value + */ + public String getClientID() + { + if (_log.isTraceEnabled()) + { + _log.trace("getClientID()"); + } + + return _clientID; + } + + /** + * Set the client id + * @param clientID The value + */ + public void setClientID(final String clientID) + { + if (_log.isTraceEnabled()) + { + _log.trace("setClientID(" + clientID + ")"); + } + + this._clientID = clientID; + } + + /** + * Get the connection type + * @return The type + */ + public int getType() + { + if (_log.isTraceEnabled()) + { + _log.trace("getType()"); + } + + return _type; + } + + /** + * Use transactions + * @return True if transacted; otherwise false + */ + public boolean isTransacted() + { + if (_log.isTraceEnabled()) + { + _log.trace("isTransacted() " + _transacted); + } + + return _transacted; + } + + /** + * Get the acknowledge mode + * @return The mode + */ + public int getAcknowledgeMode() + { + if (_log.isTraceEnabled()) + { + _log.trace("getAcknowledgeMode()"); + } + + return _acknowledgeMode; + } + + /** + * Indicates whether some other object is "equal to" this one. + * @param obj Object with which to compare + * @return True if this object is the same as the obj argument; false otherwise. + */ + @Override + public boolean equals(final Object obj) + { + if (obj instanceof QpidRAConnectionRequestInfo) + { + QpidRAConnectionRequestInfo you = (QpidRAConnectionRequestInfo)obj; + return Util.compare(_userName, you.getUserName()) && Util.compare(_password, you.getPassword()) && + Util.compare(_clientID, you.getClientID()) && + _type == you.getType() && + _transacted == you.isTransacted() && + _acknowledgeMode == you.getAcknowledgeMode(); + } + else + { + return false; + } + } + + /** + * Return the hash code for the object + * @return The hash code + */ + @Override + public int hashCode() + { + int hash = 7; + + hash += 31 * hash + (_userName != null ? _userName.hashCode() : 0); + hash += 31 * hash + (_password != null ? _password.hashCode() : 0); + hash += 31 * hash + (_clientID != null ? _clientID.hashCode() : 0); + hash += 31 * hash + _type; + hash += 31 * hash + (_transacted ? 1 : 0); + hash += 31 * hash + _acknowledgeMode; + + return hash; + } + + @Override + public String toString() + { + return "QpidRAConnectionRequestInfo[type=" + _type + + ", transacted=" + _transacted + ", acknowledgeMode=" + _acknowledgeMode + + ", clientID=" + _clientID + ", userName=" + _userName + ((_password != null) ? ", password=********]" :"]"); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRACredential.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRACredential.java new file mode 100644 index 0000000000..2b42f9dc3d --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRACredential.java @@ -0,0 +1,245 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Set; + +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnectionFactory; +import javax.resource.spi.SecurityException; +import javax.resource.spi.security.PasswordCredential; +import javax.security.auth.Subject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Credential information + * + */ +public class QpidRACredential implements Serializable +{ + /** Serial version UID */ + private static final long serialVersionUID = 7040664839205409352L; + + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRACredential.class); + + /** The user name */ + private String _userName; + + /** The password */ + private String _password; + + /** + * Private constructor + */ + private QpidRACredential() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + } + + /** + * Get the user name + * @return The value + */ + public String getUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUserName()"); + } + + return _userName; + } + + /** + * Set the user name + * @param userName The value + */ + private void setUserName(final String userName) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUserName(" + userName + ")"); + } + + this._userName = userName; + } + + /** + * Get the password + * @return The value + */ + public String getPassword() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPassword()"); + } + + return _password; + } + + /** + * Set the password + * @param password The value + */ + private void setPassword(final String password) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPassword(****)"); + } + + this._password = password; + } + + /** + * Get credentials + * @param mcf The managed connection factory + * @param subject The subject + * @param info The connection request info + * @return The credentials + * @exception SecurityException Thrown if the credentials cant be retrieved + */ + public static QpidRACredential getCredential(final ManagedConnectionFactory mcf, + final Subject subject, + final ConnectionRequestInfo info) throws SecurityException + { + if (_log.isTraceEnabled()) + { + _log.trace("getCredential(" + mcf + ", " + subject + ", " + info + ")"); + } + + QpidRACredential jc = new QpidRACredential(); + if (subject == null && info != null) + { + jc.setUserName(((QpidRAConnectionRequestInfo)info).getUserName()); + jc.setPassword(((QpidRAConnectionRequestInfo)info).getPassword()); + } + else if (subject != null) + { + PasswordCredential pwdc = GetCredentialAction.getCredential(subject, mcf); + + if (pwdc == null) + { + throw new SecurityException("No password credentials found"); + } + + jc.setUserName(pwdc.getUserName()); + jc.setPassword(new String(pwdc.getPassword())); + } + else + { + throw new SecurityException("No Subject or ConnectionRequestInfo set, could not get credentials"); + } + + return jc; + } + + /** + * String representation + * @return The representation + */ + @Override + public String toString() + { + return super.toString() + "{ username=" + _userName + ", password=**** }"; + } + + /** + * Privileged class to get credentials + */ + private static class GetCredentialAction implements PrivilegedAction<PasswordCredential> + { + /** The subject */ + private final Subject subject; + + /** The managed connection factory */ + private final ManagedConnectionFactory mcf; + + /** + * Constructor + * @param subject The subject + * @param mcf The managed connection factory + */ + GetCredentialAction(final Subject subject, final ManagedConnectionFactory mcf) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + subject + ", " + mcf + ")"); + } + + this.subject = subject; + this.mcf = mcf; + } + + /** + * Run + * @return The credential + */ + public PasswordCredential run() + { + if (_log.isTraceEnabled()) + { + _log.trace("run()"); + } + + Set<PasswordCredential> creds = subject.getPrivateCredentials(PasswordCredential.class); + PasswordCredential pwdc = null; + + for (PasswordCredential curCred : creds) + { + if (curCred.getManagedConnectionFactory().equals(mcf)) + { + pwdc = curCred; + break; + } + } + return pwdc; + } + + /** + * Get credentials + * @param subject The subject + * @param mcf The managed connection factory + * @return The credential + */ + static PasswordCredential getCredential(final Subject subject, final ManagedConnectionFactory mcf) + { + if (_log.isTraceEnabled()) + { + _log.trace("getCredential(" + subject + ", " + mcf + ")"); + } + + GetCredentialAction action = new GetCredentialAction(subject, mcf); + return AccessController.doPrivileged(action); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAException.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAException.java new file mode 100644 index 0000000000..9c070f6184 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAException.java @@ -0,0 +1,70 @@ +/* + * + * 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.qpid.ra; + + +/** + * Qpid Resource Adapter exception. + */ +public class QpidRAException extends Exception +{ + /** + * The serial version uid for this serializable class. + */ + private static final long serialVersionUID = 2921345326731695238L; + + /** + * Create a default Qpid ra exception. + */ + public QpidRAException() + { + super(); + } + + /** + * Create an Qpid ra exception with a specific message. + * @param message The message associated with this exception. + */ + public QpidRAException(final String message) + { + super(message); + } + + /** + * Create an Qpid ra exception with a specific cause. + * @param cause The cause associated with this exception. + */ + public QpidRAException(final Throwable cause) + { + super(cause); + } + + /** + * Create an Qpid ra exception with a specific message and cause. + * @param message The message associated with this exception. + * @param cause The cause associated with this exception. + */ + public QpidRAException(final String message, final Throwable cause) + { + super(message, cause); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRALocalTransaction.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRALocalTransaction.java new file mode 100644 index 0000000000..eeb49b6b52 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRALocalTransaction.java @@ -0,0 +1,129 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.resource.ResourceException; +import javax.resource.spi.LocalTransaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JMS Local transaction + * + */ +public class QpidRALocalTransaction implements LocalTransaction +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRALocalTransaction.class); + + /** The managed connection */ + private final QpidRAManagedConnection _mc; + + /** + * Constructor + * @param mc The managed connection + */ + public QpidRALocalTransaction(final QpidRAManagedConnection mc) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mc + ")"); + } + + this._mc = mc; + } + + /** + * Begin + * @exception ResourceException Thrown if the operation fails + */ + public void begin() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("begin()"); + } + } + + /** + * Commit + * @exception ResourceException Thrown if the operation fails + */ + public void commit() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("commit()"); + } + + _mc.lock(); + + try + { + if (_mc.getSession() == null) + { + throw new ResourceException("Could not commit LocalTransaction: null Session."); + } + + _mc.getSession().commit(); + } + catch (JMSException e) + { + throw new ResourceException("Could not commit LocalTransaction", e); + } + finally + { + _mc.unlock(); + } + } + + /** + * Rollback + * @exception ResourceException Thrown if the operation fails + */ + public void rollback() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("rollback()"); + } + + _mc.lock(); + try + { + if (_mc.getSession() != null && _mc.getSession().getTransacted()) + { + _mc.getSession().rollback(); + } + } + catch (JMSException ex) + { + throw new ResourceException("Could not rollback LocalTransaction", ex); + } + finally + { + _mc.unlock(); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMCFProperties.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMCFProperties.java new file mode 100644 index 0000000000..dd60c175de --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMCFProperties.java @@ -0,0 +1,177 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; + +import javax.jms.Queue; +import javax.jms.Topic; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The MCF default properties + * + */ +public class QpidRAMCFProperties extends ConnectionFactoryProperties implements Serializable +{ + /** + * Serial version UID + */ + private static final long serialVersionUID = -1675836810881223064L; + + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMCFProperties.class); + + /** + * The queue type + */ + private static final String QUEUE_TYPE = Queue.class.getName(); + + /** + * The topic type + */ + private static final String TOPIC_TYPE = Topic.class.getName(); + + /** + * The connection type + */ + private int _type = QpidRAConnectionFactory.CONNECTION; + + /** + * Use tryLock + */ + private Integer _useTryLock; + + /** + * Constructor + */ + public QpidRAMCFProperties() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + + _useTryLock = null; + } + + /** + * Get the connection type + * + * @return The type + */ + public int getType() + { + if (_log.isTraceEnabled()) + { + _log.trace("getType()"); + } + + return _type; + } + + /** + * Set the default session type. + * + * @param defaultType either javax.jms.Topic or javax.jms.Queue + */ + public void setSessionDefaultType(final String defaultType) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSessionDefaultType(" + _type + ")"); + } + + if (defaultType.equals(QpidRAMCFProperties.QUEUE_TYPE)) + { + _type = QpidRAConnectionFactory.QUEUE_CONNECTION; + } + else if (defaultType.equals(QpidRAMCFProperties.TOPIC_TYPE)) + { + _type = QpidRAConnectionFactory.TOPIC_CONNECTION; + } + else + { + _type = QpidRAConnectionFactory.CONNECTION; + } + } + + /** + * Get the default session type. + * + * @return The default session type + */ + public String getSessionDefaultType() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSessionDefaultType()"); + } + + if (_type == QpidRAConnectionFactory.CONNECTION) + { + return "BOTH"; + } + else if (_type == QpidRAConnectionFactory.QUEUE_CONNECTION) + { + return QpidRAMCFProperties.TOPIC_TYPE; + } + else + { + return QpidRAMCFProperties.QUEUE_TYPE; + } + } + + /** + * Get the useTryLock. + * + * @return the useTryLock. + */ + public Integer getUseTryLock() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUseTryLock()"); + } + + return _useTryLock; + } + + /** + * Set the useTryLock. + * + * @param useTryLock the useTryLock. + */ + public void setUseTryLock(final Integer useTryLock) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseTryLock(" + useTryLock + ")"); + } + + this._useTryLock = useTryLock; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnection.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnection.java new file mode 100644 index 0000000000..fb1b7f060c --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnection.java @@ -0,0 +1,880 @@ +/* + * + * 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.qpid.ra; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.ResourceAllocationException; +import javax.jms.Session; +import javax.jms.QueueConnection; +import javax.jms.TopicConnection; +import javax.jms.XAQueueConnection; +import javax.jms.XASession; +import javax.jms.XATopicConnection; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ConnectionEventListener; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.IllegalStateException; +import javax.resource.spi.LocalTransaction; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionMetaData; +import javax.resource.spi.SecurityException; +import javax.security.auth.Subject; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; +import javax.transaction.xa.XAResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The managed connection + * + */ +public class QpidRAManagedConnection implements ManagedConnection, ExceptionListener +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAManagedConnection.class); + + /** The managed connection factory */ + private final QpidRAManagedConnectionFactory _mcf; + + /** The connection request information */ + private final QpidRAConnectionRequestInfo _cri; + + /** The user name */ + private final String _userName; + + /** The password */ + private final String _password; + + /** Has the connection been destroyed */ + private final AtomicBoolean _isDestroyed = new AtomicBoolean(false); + + /** Event listeners */ + private final List<ConnectionEventListener> _eventListeners; + + /** Handles */ + private final Set<QpidRASessionImpl> _handles; + + /** Lock */ + private ReentrantLock _lock = new ReentrantLock(); + + // Physical JMS connection stuff + private Connection _connection; + + private XASession _xaSession; + + private XAResource _xaResource; + + private Session _session; + + private final TransactionManager _tm; + + private boolean _inManagedTx; + + /** + * Constructor + * @param mcf The managed connection factory + * @param cri The connection request information + * @param userName The user name + * @param password The password + */ + public QpidRAManagedConnection(final QpidRAManagedConnectionFactory mcf, + final QpidRAConnectionRequestInfo cri, + final TransactionManager tm, + final String userName, + final String password) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mcf + ", " + cri + ", " + userName + ", ****)"); + } + + this._mcf = mcf; + this._cri = cri; + this._tm = tm; + this._userName = userName; + this._password = password; + _eventListeners = Collections.synchronizedList(new ArrayList<ConnectionEventListener>()); + _handles = Collections.synchronizedSet(new HashSet<QpidRASessionImpl>()); + + try + { + setup(); + } + catch (Throwable t) + { + try + { + destroy(); + } + catch (Throwable ignored) + { + } + throw new ResourceException("Error during setup", t); + } + } + + /** + * Get a connection + * @param subject The security subject + * @param cxRequestInfo The request info + * @return The connection + * @exception ResourceException Thrown if an error occurs + */ + public synchronized Object getConnection(final Subject subject, final ConnectionRequestInfo cxRequestInfo) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getConnection(" + subject + ", " + cxRequestInfo + ")"); + } + + // Check user first + QpidRACredential credential = QpidRACredential.getCredential(_mcf, subject, cxRequestInfo); + + // Null users are allowed! + if (_userName != null && !_userName.equals(credential.getUserName())) + { + throw new SecurityException("Password credentials not the same, reauthentication not allowed"); + } + + if (_userName == null && credential.getUserName() != null) + { + throw new SecurityException("Password credentials not the same, reauthentication not allowed"); + } + + if (_isDestroyed.get()) + { + throw new IllegalStateException("The managed connection is already destroyed"); + } + + QpidRASessionImpl session = new QpidRASessionImpl(this, (QpidRAConnectionRequestInfo)cxRequestInfo); + _handles.add(session); + return session; + } + + /** + * Destroy all handles. + * @exception ResourceException Failed to close one or more handles. + */ + private void destroyHandles() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("destroyHandles()"); + } + + try + { + if (_connection != null) + { + _connection.stop(); + } + } + catch (Throwable t) + { + _log.trace("Ignored error stopping connection", t); + } + + for (QpidRASessionImpl session : _handles) + { + session.destroy(); + } + + _handles.clear(); + } + + /** + * Destroy the physical connection. + * @exception ResourceException Could not property close the session and connection. + */ + public void destroy() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("destroy()"); + } + + if (_isDestroyed.get() || _connection == null) + { + return; + } + + _isDestroyed.set(true); + + try + { + _connection.setExceptionListener(null); + } + catch (JMSException e) + { + _log.debug("Error unsetting the exception listener " + this, e); + } + + destroyHandles(); + + try + { + try + { + if (_xaSession != null) + { + _xaSession.close(); + } + } + catch (JMSException e) + { + _log.debug("Error closing session " + this, e); + } + + if (_connection != null) + { + _connection.close(); + } + } + catch (Throwable e) + { + throw new ResourceException("Could not properly close the session and connection", e); + } + } + + /** + * Cleanup + * @exception ResourceException Thrown if an error occurs + */ + public void cleanup() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("cleanup()"); + } + + if (_isDestroyed.get()) + { + throw new IllegalStateException("ManagedConnection already destroyed"); + } + + destroyHandles(); + + _inManagedTx = false; + + // I'm recreating the lock object when we return to the pool + // because it looks too nasty to expect the connection handle + // to unlock properly in certain race conditions + // where the dissociation of the managed connection is "random". + _lock = new ReentrantLock(); + } + + /** + * Move a handler from one mc to this one. + * @param obj An object of type QpidRASession. + * @throws ResourceException Failed to associate connection. + * @throws IllegalStateException ManagedConnection in an illegal state. + */ + public void associateConnection(final Object obj) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("associateConnection(" + obj + ")"); + } + + if (!_isDestroyed.get() && obj instanceof QpidRASessionImpl) + { + QpidRASessionImpl h = (QpidRASessionImpl)obj; + h.setManagedConnection(this); + _handles.add(h); + } + else + { + throw new IllegalStateException("ManagedConnection in an illegal state"); + } + } + + public void checkTransactionActive() throws JMSException + { + // don't bother looking at the transaction if there's an active XID + if (!_inManagedTx && _tm != null) + { + try + { + Transaction tx = _tm.getTransaction(); + if (tx != null) + { + int status = tx.getStatus(); + // Only allow states that will actually succeed + if (status != Status.STATUS_ACTIVE && status != Status.STATUS_PREPARING && + status != Status.STATUS_PREPARED && + status != Status.STATUS_COMMITTING) + { + throw new javax.jms.IllegalStateException("Transaction " + tx + " not active"); + } + } + } + catch (SystemException e) + { + JMSException jmsE = new javax.jms.IllegalStateException("Unexpected exception on the Transaction ManagerTransaction"); + jmsE.initCause(e); + throw jmsE; + } + } + } + + + /** + * Aqquire a lock on the managed connection + */ + protected void lock() + { + if (_log.isTraceEnabled()) + { + _log.trace("lock()"); + } + + _lock.lock(); + } + + /** + * Aqquire a lock on the managed connection within the specified period + * @exception JMSException Thrown if an error occurs + */ + protected void tryLock() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("tryLock()"); + } + + Integer tryLock = _mcf.getUseTryLock(); + if (tryLock == null || tryLock.intValue() <= 0) + { + lock(); + return; + } + try + { + if (_lock.tryLock(tryLock.intValue(), TimeUnit.SECONDS) == false) + { + throw new ResourceAllocationException("Unable to obtain lock in " + tryLock + " seconds: " + this); + } + } + catch (InterruptedException e) + { + throw new ResourceAllocationException("Interrupted attempting lock: " + this); + } + } + + /** + * Unlock the managed connection + */ + protected void unlock() + { + if (_log.isTraceEnabled()) + { + _log.trace("unlock()"); + } + + if (_lock.isHeldByCurrentThread()) + { + _lock.unlock(); + } + } + + /** + * Add a connection event listener. + * @param l The connection event listener to be added. + */ + public void addConnectionEventListener(final ConnectionEventListener l) + { + if (_log.isTraceEnabled()) + { + _log.trace("addConnectionEventListener(" + l + ")"); + } + + _eventListeners.add(l); + } + + /** + * Remove a connection event listener. + * @param l The connection event listener to be removed. + */ + public void removeConnectionEventListener(final ConnectionEventListener l) + { + if (_log.isTraceEnabled()) + { + _log.trace("removeConnectionEventListener(" + l + ")"); + } + + _eventListeners.remove(l); + } + + /** + * Get the XAResource for the connection. + * @return The XAResource for the connection. + * @exception ResourceException XA transaction not supported + */ + public XAResource getXAResource() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getXAResource()"); + } + + // + // Spec says a mc must allways return the same XA resource, + // so we cache it. + // + if (_xaResource == null) + { + _xaResource = new QpidRAXAResource(this, _xaSession.getXAResource()); + } + + if (_log.isTraceEnabled()) + { + _log.trace("XAResource=" + _xaResource); + } + + return _xaResource; + } + + /** + * Get the location transaction for the connection. + * @return The local transaction for the connection. + * @exception ResourceException Thrown if operation fails. + */ + public LocalTransaction getLocalTransaction() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getLocalTransaction()"); + } + + LocalTransaction tx = new QpidRALocalTransaction(this); + + if (_log.isTraceEnabled()) + { + _log.trace("LocalTransaction=" + tx); + } + + return tx; + } + + /** + * Get the meta data for the connection. + * @return The meta data for the connection. + * @exception ResourceException Thrown if the operation fails. + * @exception IllegalStateException Thrown if the managed connection already is destroyed. + */ + public ManagedConnectionMetaData getMetaData() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMetaData()"); + } + + if (_isDestroyed.get()) + { + throw new IllegalStateException("The managed connection is already destroyed"); + } + + return new QpidRAMetaData(this); + } + + /** + * Set the log writer -- NOT SUPPORTED + * @param out The log writer + * @exception ResourceException If operation fails + */ + public void setLogWriter(final PrintWriter out) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setLogWriter(" + out + ")"); + } + } + + /** + * Get the log writer -- NOT SUPPORTED + * @return Always null + * @exception ResourceException If operation fails + */ + public PrintWriter getLogWriter() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getLogWriter()"); + } + + return null; + } + + /** + * Notifies user of a JMS exception. + * @param exception The JMS exception + */ + public void onException(final JMSException exception) + { + if (_log.isTraceEnabled()) + { + _log.trace("onException(" + exception + ")"); + } + + if (_isDestroyed.get()) + { + if (_log.isTraceEnabled()) + { + _log.trace("Ignoring error on already destroyed connection " + this, exception); + } + return; + } + + _log.warn("Handling JMS exception failure: " + this, exception); + + try + { + _connection.setExceptionListener(null); + } + catch (JMSException e) + { + _log.debug("Unable to unset exception listener", e); + } + + ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, exception); + sendEvent(event); + } + + /** + * Get the session for this connection. + * @return The session + * @throws JMSException + */ + protected Session getSession() throws JMSException + { + if(_xaSession != null && !_mcf.getUseLocalTx()) + { + if (_log.isTraceEnabled()) + { + _log.trace("getSession() -> XA session " + Util.asString(_xaSession)); + } + + return _xaSession; + } + else + { + if (_log.isTraceEnabled()) + { + _log.trace("getSession() -> session " + Util.asString(_session)); + } + + return _session; + } + } + + /** + * Send an event. + * @param event The event to send. + */ + protected void sendEvent(final ConnectionEvent event) + { + if (_log.isTraceEnabled()) + { + _log.trace("sendEvent(" + event + ")"); + } + + int type = event.getId(); + + // convert to an array to avoid concurrent modification exceptions + ConnectionEventListener[] list = _eventListeners.toArray(new ConnectionEventListener[_eventListeners.size()]); + + for (ConnectionEventListener l : list) + { + switch (type) + { + case ConnectionEvent.CONNECTION_CLOSED: + l.connectionClosed(event); + break; + + case ConnectionEvent.LOCAL_TRANSACTION_STARTED: + l.localTransactionStarted(event); + break; + + case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED: + l.localTransactionCommitted(event); + break; + + case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK: + l.localTransactionRolledback(event); + break; + + case ConnectionEvent.CONNECTION_ERROR_OCCURRED: + l.connectionErrorOccurred(event); + break; + + default: + throw new IllegalArgumentException("Illegal eventType: " + type); + } + } + } + + /** + * Remove a handle from the handle map. + * @param handle The handle to remove. + */ + protected void removeHandle(final QpidRASessionImpl handle) + { + if (_log.isTraceEnabled()) + { + _log.trace("removeHandle(" + handle + ")"); + } + + _handles.remove(handle); + } + + /** + * Get the request info for this connection. + * @return The connection request info for this connection. + */ + protected QpidRAConnectionRequestInfo getCRI() + { + if (_log.isTraceEnabled()) + { + _log.trace("getCRI()"); + } + + return _cri; + } + + /** + * Get the connection factory for this connection. + * @return The connection factory for this connection. + */ + protected QpidRAManagedConnectionFactory getManagedConnectionFactory() + { + if (_log.isTraceEnabled()) + { + _log.trace("getManagedConnectionFactory()"); + } + + return _mcf; + } + + /** + * Start the connection + * @exception JMSException Thrown if the connection cant be started + */ + void start() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("start()"); + } + + if (_connection != null) + { + _connection.start(); + } + } + + /** + * Stop the connection + * @exception JMSException Thrown if the connection cant be stopped + */ + void stop() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("stop()"); + } + + if (_connection != null) + { + _connection.stop(); + } + } + + /** + * Get the user name + * @return The user name + */ + protected String getUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUserName()"); + } + + return _userName; + } + + /** + * Setup the connection. + * @exception ResourceException Thrown if a connection couldnt be created + */ + private void setup() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setup()"); + } + + try + { + boolean transacted = _cri.isTransacted(); + int acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + boolean localTx = _mcf.getUseLocalTx(); + + if (_cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION) + { + if (_userName != null && _password != null) + { + if(!localTx) + { + _connection = _mcf.getCleanAMQConnectionFactory().createXATopicConnection(_userName, _password); + } + else + { + _connection = _mcf.getCleanAMQConnectionFactory().createTopicConnection(); + } + } + else + { + if(!localTx) + { + _connection = _mcf.getDefaultAMQConnectionFactory().createXATopicConnection(); + } + else + { + _connection = _mcf.getDefaultAMQConnectionFactory().createTopicConnection(); + } + } + + if(!localTx) + { + _xaSession = ((XATopicConnection)_connection).createXATopicSession(); + + } + else + { + _session = ((TopicConnection)_connection).createTopicSession(localTx, acknowledgeMode); + } + } + else if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION) + { + if (_userName != null && _password != null) + { + if(!localTx) + { + _connection = _mcf.getCleanAMQConnectionFactory().createXAQueueConnection(_userName, _password); + } + else + { + _connection = _mcf.getCleanAMQConnectionFactory().createQueueConnection(); + } + } + else + { + if(!localTx) + { + _connection = _mcf.getDefaultAMQConnectionFactory().createXAQueueConnection(); + } + else + { + _connection = _mcf.getDefaultAMQConnectionFactory().createQueueConnection(); + } + } + + if(!localTx) + { + _xaSession = ((XAQueueConnection)_connection).createXAQueueSession(); + + } + else + { + _session = ((QueueConnection)_connection).createQueueSession(localTx, acknowledgeMode); + + } + } + else + { + if (_userName != null && _password != null) + { + if(!localTx) + { + _connection = _mcf.getCleanAMQConnectionFactory().createXAConnection(_userName, _password); + } + else + { + _connection = _mcf.getCleanAMQConnectionFactory().createConnection(); + } + } + else + { + if(!localTx) + { + _connection = _mcf.getDefaultAMQConnectionFactory().createXAConnection(); + } + else + { + _connection = _mcf.getDefaultAMQConnectionFactory().createConnection(); + } + } + + if(!localTx) + { + _xaSession = ((XAQueueConnection)_connection).createXASession(); + + } + else + { + _session = ((QueueConnection)_connection).createSession(localTx, acknowledgeMode); + + } + } + + _connection.setExceptionListener(this); + } + catch (JMSException je) + { + throw new ResourceException(je.getMessage(), je); + } + } + + protected void setInManagedTx(boolean inManagedTx) + { + this._inManagedTx = inManagedTx; + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnectionFactory.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnectionFactory.java new file mode 100644 index 0000000000..377a0c6253 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAManagedConnectionFactory.java @@ -0,0 +1,623 @@ +/* + * + * 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.qpid.ra; + +import java.io.PrintWriter; +import java.util.Set; + +import javax.jms.ConnectionMetaData; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionManager; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; +import javax.resource.spi.ResourceAdapter; +import javax.resource.spi.ResourceAdapterAssociation; +import javax.security.auth.Subject; + +import org.apache.qpid.client.AMQConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Qpid ManagedConectionFactory + * + */ +public class QpidRAManagedConnectionFactory implements ManagedConnectionFactory, ResourceAdapterAssociation +{ + /** + * Serial version UID + */ + private static final long serialVersionUID = -8798804592247643959L; + + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAManagedConnectionFactory.class); + + /** + * The resource adapter + */ + private QpidResourceAdapter _ra; + + /** + * Connection manager + */ + private ConnectionManager _cm; + + /** + * The managed connection factory properties + */ + private final QpidRAMCFProperties _mcfProperties; + + /** + * Connection Factory used if properties are set + */ + private AMQConnectionFactory _connectionFactory; + + /** + * Constructor + */ + public QpidRAManagedConnectionFactory() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + + _ra = null; + _cm = null; + _mcfProperties = new QpidRAMCFProperties(); + } + + /** + * Creates a Connection Factory instance + * + * @return javax.resource.cci.ConnectionFactory instance + * @throws ResourceException Thrown if a connection factory cant be created + */ + public Object createConnectionFactory() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionFactory()"); + } + + return createConnectionFactory(new QpidRAConnectionManager()); + } + + /** + * Creates a Connection Factory instance + * + * @param cxManager The connection manager + * @return javax.resource.cci.ConnectionFactory instance + * @throws ResourceException Thrown if a connection factory cant be created + */ + public Object createConnectionFactory(final ConnectionManager cxManager) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionFactory(" + cxManager + ")"); + } + + _cm = cxManager; + + QpidRAConnectionFactory cf = new QpidRAConnectionFactoryImpl(this, _cm); + + if (_log.isTraceEnabled()) + { + _log.trace("Created connection factory: " + cf + + ", using connection manager: " + + _cm); + } + + return cf; + } + + /** + * Creates a new physical connection to the underlying EIS resource manager. + * + * @param subject Caller's security information + * @param cxRequestInfo Additional resource adapter specific connection request information + * @return The managed connection + * @throws ResourceException Thrown if a managed connection cant be created + */ + public ManagedConnection createManagedConnection(final Subject subject, final ConnectionRequestInfo cxRequestInfo) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("createManagedConnection(" + subject + ", " + cxRequestInfo + ")"); + } + + QpidRAConnectionRequestInfo cri = getCRI((QpidRAConnectionRequestInfo)cxRequestInfo); + + QpidRACredential credential = QpidRACredential.getCredential(this, subject, cri); + + if (_log.isTraceEnabled()) + { + _log.trace("jms credential: " + credential); + } + + QpidRAManagedConnection mc = new QpidRAManagedConnection(this, + cri, + _ra.getTM(), + credential.getUserName(), + credential.getPassword()); + + if (_log.isTraceEnabled()) + { + _log.trace("created new managed connection: " + mc); + } + + return mc; + } + + /** + * Returns a matched connection from the candidate set of connections. + * + * @param connectionSet The candidate connection set + * @param subject Caller's security information + * @param cxRequestInfo Additional resource adapter specific connection request information + * @return The managed connection + * @throws ResourceException Thrown if no managed connection can be found + */ + @SuppressWarnings("rawtypes") + public ManagedConnection matchManagedConnections(final Set connectionSet, + final Subject subject, + final ConnectionRequestInfo cxRequestInfo) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("matchManagedConnections(" + connectionSet + + ", " + + subject + + ", " + + cxRequestInfo + + ")"); + } + + QpidRAConnectionRequestInfo cri = getCRI((QpidRAConnectionRequestInfo)cxRequestInfo); + QpidRACredential credential = QpidRACredential.getCredential(this, subject, cri); + + if (_log.isTraceEnabled()) + { + _log.trace("Looking for connection matching credentials: " + credential); + } + + for (final Object obj : connectionSet) + { + if (obj instanceof QpidRAManagedConnection) + { + QpidRAManagedConnection mc = (QpidRAManagedConnection)obj; + ManagedConnectionFactory mcf = mc.getManagedConnectionFactory(); + + if ((mc.getUserName() == null || mc.getUserName() != null && mc.getUserName() + .equals(credential.getUserName())) && mcf.equals(this)) + { + if (cri.equals(mc.getCRI())) + { + if (_log.isTraceEnabled()) + { + _log.trace("Found matching connection: " + mc); + } + + return mc; + } + } + } + } + + if (_log.isTraceEnabled()) + { + _log.trace("No matching connection was found"); + } + + return null; + } + + /** + * Set the log writer -- NOT SUPPORTED + * + * @param out The writer + * @throws ResourceException Thrown if the writer cant be set + */ + public void setLogWriter(final PrintWriter out) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setLogWriter(" + out + ")"); + } + } + + /** + * Get the log writer -- NOT SUPPORTED + * + * @return The writer + * @throws ResourceException Thrown if the writer cant be retrieved + */ + public PrintWriter getLogWriter() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getLogWriter()"); + } + + return null; + } + + /** + * Get the resource adapter + * + * @return The resource adapter + */ + public ResourceAdapter getResourceAdapter() + { + if (_log.isTraceEnabled()) + { + _log.trace("getResourceAdapter()"); + } + + return _ra; + } + + /** + * Set the resource adapter + * + * @param ra The resource adapter + * @throws ResourceException Thrown if incorrect resource adapter + */ + public void setResourceAdapter(final ResourceAdapter ra) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setResourceAdapter(" + ra + ")"); + } + + if (!(ra instanceof QpidResourceAdapter)) + { + throw new ResourceException("Resource adapter is " + ra); + } + + this._ra = (QpidResourceAdapter)ra; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param obj Object with which to compare + * @return True if this object is the same as the obj argument; false otherwise. + */ + @Override + public boolean equals(final Object obj) + { + if (obj instanceof QpidRAManagedConnectionFactory) + { + QpidRAManagedConnectionFactory other = (QpidRAManagedConnectionFactory)obj; + + return _mcfProperties.equals(other.getProperties()) && _ra.equals(other.getResourceAdapter()); + } + else + { + return false; + } + } + + /** + * Return the hash code for the object + * + * @return The hash code + */ + @Override + public int hashCode() + { + int hash = _mcfProperties.hashCode(); + hash += 31 * _ra.hashCode(); + + return hash; + } + + /** + * Get the default session type + * + * @return The value + */ + public String getSessionDefaultType() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSessionDefaultType()"); + } + + return _mcfProperties.getSessionDefaultType(); + } + + /** + * Set the default session type + * + * @param type either javax.jms.Topic or javax.jms.Queue + */ + public void setSessionDefaultType(final String type) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSessionDefaultType(" + type + ")"); + } + + _mcfProperties.setSessionDefaultType(type); + } + + public String getClientID() + { + return _mcfProperties.getClientId(); + } + + public void setClientID(final String clientID) + { + _mcfProperties.setClientId(clientID); + } + + public String getConnectionURL() + { + return _mcfProperties.getConnectionURL() ; + } + + public void setConnectionURL(final String connectionURL) + { + _mcfProperties.setConnectionURL(connectionURL); + } + + public String getPassword() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDefaultPassword()"); + } + return _mcfProperties.getPassword(); + } + + public void setPassword(final String defaultPassword) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDefaultPassword(" + defaultPassword + ")"); + } + _mcfProperties.setPassword(defaultPassword); + } + + public String getUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDefaultUsername()"); + } + return _mcfProperties.getUserName(); + } + + public void setUserName(final String defaultUsername) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDefaultUsername(" + defaultUsername + ")"); + } + _mcfProperties.setUserName(defaultUsername); + } + + public String getHost() + { + if (_log.isTraceEnabled()) + { + _log.trace("getHost()"); + } + return _mcfProperties.getHost(); + } + + public void setHost(final String host) + { + if (_log.isTraceEnabled()) + { + _log.trace("setHost(" + host + ")"); + } + _mcfProperties.setHost(host); + } + + public Integer getPort() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPort()"); + } + return _mcfProperties.getPort(); + } + + public void setPort(final Integer port) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPort(" + port + ")"); + } + _mcfProperties.setPort(port); + } + + public String getPath() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPath()"); + } + return _mcfProperties.getPath(); + } + + public void setPath(final String path) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPath(" + path + ")"); + } + _mcfProperties.setPath(path); + } + + /** + * Get the useTryLock. + * + * @return the useTryLock. + */ + public Integer getUseTryLock() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUseTryLock()"); + } + + return _mcfProperties.getUseTryLock(); + } + + /** + * Set the useTryLock. + * + * @param useTryLock the useTryLock. + */ + public void setUseTryLock(final Integer useTryLock) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseTryLock(" + useTryLock + ")"); + } + + _mcfProperties.setUseTryLock(useTryLock); + } + + /** + * Get the connection metadata + * + * @return The metadata + */ + public ConnectionMetaData getMetaData() + { + if (_log.isTraceEnabled()) + { + _log.trace("getMetadata()"); + } + + return new QpidRAConnectionMetaData(); + } + + /** + * Get the default connection factory + * + * @return The factory + */ + protected synchronized AMQConnectionFactory getDefaultAMQConnectionFactory() throws ResourceException + { + if (_connectionFactory == null) + { + try + { + _connectionFactory = _ra.createAMQConnectionFactory(_mcfProperties); + } + catch (final QpidRAException qpidrae) + { + throw new ResourceException("Unexpected exception creating the connection factory", qpidrae) ; + } + } + return _connectionFactory; + } + + /** + * Get a clean connection factory + * + * @return The factory + */ + protected AMQConnectionFactory getCleanAMQConnectionFactory() throws ResourceException + { + try + { + return _ra.createAMQConnectionFactory(_mcfProperties); + } + catch (final QpidRAException qpidrae) + { + throw new ResourceException("Unexpected exception creating the connection factory", qpidrae) ; + } + } + + /** + * Get the managed connection factory properties + * + * @return The properties + */ + protected QpidRAMCFProperties getProperties() + { + if (_log.isTraceEnabled()) + { + _log.trace("getProperties()"); + } + + return _mcfProperties; + } + + /** + * Get a connection request info instance + * + * @param info The instance that should be updated; may be <code>null</code> + * @return The instance + * @throws ResourceException + */ + private QpidRAConnectionRequestInfo getCRI(final QpidRAConnectionRequestInfo info) + throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getCRI(" + info + ")"); + } + + if (info == null) + { + // Create a default one + return new QpidRAConnectionRequestInfo(_ra, _mcfProperties.getType()); + } + else + { + // Fill the one with any defaults + info.setDefaults(_ra); + return info; + } + } + + public Boolean getUseLocalTx() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUseLocalTx()"); + } + + return _mcfProperties.isUseLocalTx(); + } + + public void setUseLocalTx(final Boolean localTx) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseLocalTx(" + localTx + ")"); + } + + _mcfProperties.setUseLocalTx(localTx); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMapMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMapMessage.java new file mode 100644 index 0000000000..797a28a09a --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMapMessage.java @@ -0,0 +1,457 @@ +/* + * + * 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.qpid.ra; + +import java.util.Enumeration; + +import javax.jms.JMSException; +import javax.jms.MapMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + * + */ +public class QpidRAMapMessage extends QpidRAMessage implements MapMessage +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMapMessage.class); + + /** + * Create a new wrapper + * + * @param message the message + * @param session the session + */ + public QpidRAMapMessage(final MapMessage message, final QpidRASessionImpl session) + { + super(message, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + message + ", " + session + ")"); + } + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean getBoolean(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getBoolean(" + name + ")"); + } + + return ((MapMessage)_message).getBoolean(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte getByte(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getByte(" + name + ")"); + } + + return ((MapMessage)_message).getByte(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte[] getBytes(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getBytes(" + name + ")"); + } + + return ((MapMessage)_message).getBytes(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public char getChar(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getChar(" + name + ")"); + } + + return ((MapMessage)_message).getChar(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public double getDouble(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDouble(" + name + ")"); + } + + return ((MapMessage)_message).getDouble(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public float getFloat(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getFloat(" + name + ")"); + } + + return ((MapMessage)_message).getFloat(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int getInt(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getInt(" + name + ")"); + } + + return ((MapMessage)_message).getInt(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long getLong(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getLong(" + name + ")"); + } + + return ((MapMessage)_message).getLong(name); + } + + /** + * Get the map names + * @return The values + * @exception JMSException Thrown if an error occurs + */ + public Enumeration<?> getMapNames() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMapNames()"); + } + + return ((MapMessage)_message).getMapNames(); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public Object getObject(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getObject(" + name + ")"); + } + + return ((MapMessage)_message).getObject(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public short getShort(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getShort(" + name + ")"); + } + + return ((MapMessage)_message).getShort(name); + } + + /** + * Get + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String getString(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getString(" + name + ")"); + } + + return ((MapMessage)_message).getString(name); + } + + /** + * Does the item exist + * @param name The name + * @return True / false + * @exception JMSException Thrown if an error occurs + */ + public boolean itemExists(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("itemExists(" + name + ")"); + } + + return ((MapMessage)_message).itemExists(name); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setBoolean(final String name, final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setBoolean(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setBoolean(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setByte(final String name, final byte value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setByte(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setByte(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @param offset The offset + * @param length The length + * @exception JMSException Thrown if an error occurs + */ + public void setBytes(final String name, final byte[] value, final int offset, final int length) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setBytes(" + name + ", " + value + ", " + offset + ", " + length + ")"); + } + + ((MapMessage)_message).setBytes(name, value, offset, length); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setBytes(final String name, final byte[] value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setBytes(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setBytes(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setChar(final String name, final char value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setChar(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setChar(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setDouble(final String name, final double value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDouble(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setDouble(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setFloat(final String name, final float value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setFloat(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setFloat(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setInt(final String name, final int value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setInt(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setInt(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setLong(final String name, final long value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setLong(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setLong(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setObject(final String name, final Object value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setObject(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setObject(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setShort(final String name, final short value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setShort(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setShort(name, value); + } + + /** + * Set + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setString(final String name, final String value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setString(" + name + ", " + value + ")"); + } + + ((MapMessage)_message).setString(name, value); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessage.java new file mode 100644 index 0000000000..691fe8c40a --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessage.java @@ -0,0 +1,782 @@ +/* + * + * 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.qpid.ra; + +import java.util.Enumeration; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + */ +public class QpidRAMessage implements Message +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMessage.class); + + /** The message */ + protected Message _message; + + /** The session */ + protected QpidRASessionImpl _session; + + /** + * Create a new wrapper + * @param message the message + * @param session the session + */ + public QpidRAMessage(final Message message, final QpidRASessionImpl session) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(message) + ", " + session + ")"); + } + + this._message = message; + this._session = session; + } + + /** + * Acknowledge + * @exception JMSException Thrown if an error occurs + */ + public void acknowledge() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("acknowledge()"); + } + + _session.getSession(); // Check for closed + _message.acknowledge(); + } + + /** + * Clear body + * @exception JMSException Thrown if an error occurs + */ + public void clearBody() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("clearBody()"); + } + + _message.clearBody(); + } + + /** + * Clear properties + * @exception JMSException Thrown if an error occurs + */ + public void clearProperties() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("clearProperties()"); + } + + _message.clearProperties(); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean getBooleanProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getBooleanProperty(" + name + ")"); + } + + return _message.getBooleanProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte getByteProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getByteProperty(" + name + ")"); + } + + return _message.getByteProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public double getDoubleProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDoubleProperty(" + name + ")"); + } + + return _message.getDoubleProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public float getFloatProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getFloatProperty(" + name + ")"); + } + + return _message.getFloatProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int getIntProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getIntProperty(" + name + ")"); + } + + return _message.getIntProperty(name); + } + + /** + * Get correlation id + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String getJMSCorrelationID() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSCorrelationID()"); + } + + return _message.getJMSCorrelationID(); + } + + /** + * Get correlation id + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte[] getJMSCorrelationIDAsBytes() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSCorrelationIDAsBytes()"); + } + + return _message.getJMSCorrelationIDAsBytes(); + } + + /** + * Get delivery mode + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int getJMSDeliveryMode() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSDeliveryMode()"); + } + + return _message.getJMSDeliveryMode(); + } + + /** + * Get destination + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public Destination getJMSDestination() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSDestination()"); + } + + return _message.getJMSDestination(); + } + + /** + * Get expiration + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long getJMSExpiration() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSExpiration()"); + } + + return _message.getJMSExpiration(); + } + + /** + * Get message id + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String getJMSMessageID() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSMessageID()"); + } + + return _message.getJMSMessageID(); + } + + /** + * Get priority + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int getJMSPriority() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSPriority()"); + } + + return _message.getJMSPriority(); + } + + /** + * Get redelivered status + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean getJMSRedelivered() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSRedelivered()"); + } + + return _message.getJMSRedelivered(); + } + + /** + * Get reply to destination + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public Destination getJMSReplyTo() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSReplyTo()"); + } + + return _message.getJMSReplyTo(); + } + + /** + * Get timestamp + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long getJMSTimestamp() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSTimestamp()"); + } + + return _message.getJMSTimestamp(); + } + + /** + * Get type + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String getJMSType() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getJMSType()"); + } + + return _message.getJMSType(); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long getLongProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getLongProperty(" + name + ")"); + } + + return _message.getLongProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public Object getObjectProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getObjectProperty(" + name + ")"); + } + + return _message.getObjectProperty(name); + } + + /** + * Get property names + * @return The values + * @exception JMSException Thrown if an error occurs + */ + public Enumeration<?> getPropertyNames() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getPropertyNames()"); + } + + return _message.getPropertyNames(); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public short getShortProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getShortProperty(" + name + ")"); + } + + return _message.getShortProperty(name); + } + + /** + * Get property + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String getStringProperty(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getStringProperty(" + name + ")"); + } + + return _message.getStringProperty(name); + } + + /** + * Do property exist + * @param name The name + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean propertyExists(final String name) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("propertyExists(" + name + ")"); + } + + return _message.propertyExists(name); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setBooleanProperty(final String name, final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setBooleanProperty(" + name + ", " + value + ")"); + } + + _message.setBooleanProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setByteProperty(final String name, final byte value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setByteProperty(" + name + ", " + value + ")"); + } + + _message.setByteProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setDoubleProperty(final String name, final double value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDoubleProperty(" + name + ", " + value + ")"); + } + + _message.setDoubleProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setFloatProperty(final String name, final float value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setFloatProperty(" + name + ", " + value + ")"); + } + + _message.setFloatProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setIntProperty(final String name, final int value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setIntProperty(" + name + ", " + value + ")"); + } + + _message.setIntProperty(name, value); + } + + /** + * Set correlation id + * @param correlationID The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSCorrelationID(final String correlationID) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSCorrelationID(" + correlationID + ")"); + } + + _message.setJMSCorrelationID(correlationID); + } + + /** + * Set correlation id + * @param correlationID The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSCorrelationIDAsBytes(final byte[] correlationID) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSCorrelationIDAsBytes(" + correlationID + ")"); + } + + _message.setJMSCorrelationIDAsBytes(correlationID); + } + + /** + * Set delivery mode + * @param deliveryMode The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSDeliveryMode(final int deliveryMode) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSDeliveryMode(" + deliveryMode + ")"); + } + + _message.setJMSDeliveryMode(deliveryMode); + } + + /** + * Set destination + * @param destination The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSDestination(final Destination destination) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSDestination(" + destination + ")"); + } + + _message.setJMSDestination(destination); + } + + /** + * Set expiration + * @param expiration The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSExpiration(final long expiration) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSExpiration(" + expiration + ")"); + } + + _message.setJMSExpiration(expiration); + } + + /** + * Set message id + * @param id The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSMessageID(final String id) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSMessageID(" + id + ")"); + } + + _message.setJMSMessageID(id); + } + + /** + * Set priority + * @param priority The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSPriority(final int priority) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSPriority(" + priority + ")"); + } + + _message.setJMSPriority(priority); + } + + /** + * Set redelivered status + * @param redelivered The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSRedelivered(final boolean redelivered) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSRedelivered(" + redelivered + ")"); + } + + _message.setJMSRedelivered(redelivered); + } + + /** + * Set reply to + * @param replyTo The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSReplyTo(final Destination replyTo) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSReplyTo(" + replyTo + ")"); + } + + _message.setJMSReplyTo(replyTo); + } + + /** + * Set timestamp + * @param timestamp The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSTimestamp(final long timestamp) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSTimestamp(" + timestamp + ")"); + } + + _message.setJMSTimestamp(timestamp); + } + + /** + * Set type + * @param type The value + * @exception JMSException Thrown if an error occurs + */ + public void setJMSType(final String type) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setJMSType(" + type + ")"); + } + + _message.setJMSType(type); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setLongProperty(final String name, final long value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setLongProperty(" + name + ", " + value + ")"); + } + + _message.setLongProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setObjectProperty(final String name, final Object value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setObjectProperty(" + name + ", " + value + ")"); + } + + _message.setObjectProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setShortProperty(final String name, final short value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setShortProperty(" + name + ", " + value + ")"); + } + + _message.setShortProperty(name, value); + } + + /** + * Set property + * @param name The name + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setStringProperty(final String name, final String value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setStringProperty(" + name + ", " + value + ")"); + } + + _message.setStringProperty(name, value); + } + + /** + * Return the hash code + * @return The hash code + */ + @Override + public int hashCode() + { + return _message.hashCode(); + } + + /** + * Check for equality + * @param object The other object + * @return True / false + */ + @Override + public boolean equals(final Object object) + { + if (object != null && object instanceof QpidRAMessage) + { + return _message.equals(((QpidRAMessage)object)._message); + } + else + { + return _message.equals(object); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageConsumer.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageConsumer.java new file mode 100644 index 0000000000..aa0e6adb0e --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageConsumer.java @@ -0,0 +1,353 @@ +/* + * + * 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.qpid.ra; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message consumer + * + */ +public class QpidRAMessageConsumer implements MessageConsumer +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMessageConsumer.class); + + /** The wrapped message consumer */ + protected MessageConsumer _consumer; + /** The closed flag */ + private AtomicBoolean _closed = new AtomicBoolean(); + + /** The session for this consumer */ + protected QpidRASessionImpl _session; + + /** + * Create a new wrapper + * @param consumer the consumer + * @param session the session + */ + public QpidRAMessageConsumer(final MessageConsumer consumer, final QpidRASessionImpl session) + { + this._consumer = consumer; + this._session = session; + + if (_log.isTraceEnabled()) + { + _log.trace("new QpidRAMessageConsumer " + this + + " consumer=" + + Util.asString(consumer) + + " session=" + + session); + } + } + + /** + * Close + * @exception JMSException Thrown if an error occurs + */ + public void close() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("close " + this); + } + try + { + closeConsumer(); + } + finally + { + _session.removeConsumer(this); + } + } + + /** + * Check state + * @exception JMSException Thrown if an error occurs + */ + void checkState() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("checkState()"); + } + _session.checkState(); + } + + /** + * Get message listener + * @return The listener + * @exception JMSException Thrown if an error occurs + */ + public MessageListener getMessageListener() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageListener()"); + } + + checkState(); + _session.checkStrict(); + return _consumer.getMessageListener(); + } + + /** + * Set message listener + * @param listener The listener + * @exception JMSException Thrown if an error occurs + */ + public void setMessageListener(final MessageListener listener) throws JMSException + { + _session.lock(); + try + { + checkState(); + _session.checkStrict(); + if (listener == null) + { + _consumer.setMessageListener(null); + } + else + { + _consumer.setMessageListener(wrapMessageListener(listener)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Get message selector + * @return The selector + * @exception JMSException Thrown if an error occurs + */ + public String getMessageSelector() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageSelector()"); + } + + checkState(); + return _consumer.getMessageSelector(); + } + + /** + * Receive + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public Message receive() throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("receive " + this); + } + + checkState(); + // Make an explicit start check otherwise qpid starts the dispatcher + Message message = (_session.isStarted() ? _consumer.receive() : null); + + if (_log.isTraceEnabled()) + { + _log.trace("received " + this + " result=" + Util.asString(message)); + } + + if (message == null) + { + return null; + } + else + { + return wrapMessage(message); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Receive + * @param timeout The timeout value + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public Message receive(final long timeout) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("receive " + this + " timeout=" + timeout); + } + + checkState(); + // Make an explicit start check otherwise qpid starts the dispatcher + Message message = (_session.isStarted() ? _consumer.receive(timeout) : null); + + if (_log.isTraceEnabled()) + { + _log.trace("received " + this + " result=" + Util.asString(message)); + } + + if (message == null) + { + return null; + } + else + { + return wrapMessage(message); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Receive + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public Message receiveNoWait() throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("receiveNoWait " + this); + } + + checkState(); + // Make an explicit start check otherwise qpid starts the dispatcher + Message message = (_session.isStarted() ? _consumer.receiveNoWait() : null); + + if (_log.isTraceEnabled()) + { + _log.trace("received " + this + " result=" + Util.asString(message)); + } + + if (message == null) + { + return null; + } + else + { + return wrapMessage(message); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Close consumer + * @exception JMSException Thrown if an error occurs + */ + void closeConsumer() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("closeConsumer()"); + } + + if (!_closed.getAndSet(true)) + { + _consumer.close(); + } + } + + /** + * Wrap message + * @param message The message to be wrapped + * @return The wrapped message + */ + Message wrapMessage(final Message message) + { + if (_log.isTraceEnabled()) + { + _log.trace("wrapMessage(" + Util.asString(message) + ")"); + } + + if (message instanceof BytesMessage) + { + return new QpidRABytesMessage((BytesMessage)message, _session); + } + else if (message instanceof MapMessage) + { + return new QpidRAMapMessage((MapMessage)message, _session); + } + else if (message instanceof ObjectMessage) + { + return new QpidRAObjectMessage((ObjectMessage)message, _session); + } + else if (message instanceof StreamMessage) + { + return new QpidRAStreamMessage((StreamMessage)message, _session); + } + else if (message instanceof TextMessage) + { + return new QpidRATextMessage((TextMessage)message, _session); + } + return new QpidRAMessage(message, _session); + } + + /** + * Wrap message listener + * @param listener The listener to be wrapped + * @return The wrapped listener + */ + MessageListener wrapMessageListener(final MessageListener listener) + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageSelector()"); + } + + return new QpidRAMessageListener(listener, this); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageListener.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageListener.java new file mode 100644 index 0000000000..e272df818f --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageListener.java @@ -0,0 +1,74 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.Message; +import javax.jms.MessageListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message listener + */ +public class QpidRAMessageListener implements MessageListener +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMessageListener.class); + + /** The message listener */ + private final MessageListener _listener; + + /** The consumer */ + private final QpidRAMessageConsumer _consumer; + + /** + * Create a new wrapper + * @param listener the listener + * @param consumer the consumer + */ + public QpidRAMessageListener(final MessageListener listener, final QpidRAMessageConsumer consumer) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + listener + ", " + consumer + ")"); + } + + this._listener = listener; + this._consumer = consumer; + } + + /** + * On message + * @param message The message + */ + public void onMessage(Message message) + { + if (_log.isTraceEnabled()) + { + _log.trace("onMessage(" + Util.asString(message) + ")"); + } + + message = _consumer.wrapMessage(message); + _listener.onMessage(message); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageProducer.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageProducer.java new file mode 100644 index 0000000000..ce0656e30d --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMessageProducer.java @@ -0,0 +1,419 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QpidRAMessageProducer. + * + */ +public class QpidRAMessageProducer implements MessageProducer +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMessageProducer.class); + + /** The wrapped message producer */ + protected MessageProducer _producer; + + /** The session for this consumer */ + protected QpidRASessionImpl _session; + + /** + * Create a new wrapper + * @param producer the producer + * @param session the session + */ + public QpidRAMessageProducer(final MessageProducer producer, final QpidRASessionImpl session) + { + this._producer = producer; + this._session = session; + + if (_log.isTraceEnabled()) + { + _log.trace("new QpidRAMessageProducer " + this + + " producer=" + + Util.asString(producer) + + " session=" + + session); + } + } + + /** + * Close + * @exception JMSException Thrown if an error occurs + */ + public void close() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("close " + this); + } + try + { + closeProducer(); + } + finally + { + _session.removeProducer(this); + } + } + + /** + * Send message + * @param destination The destination + * @param message The message + * @param deliveryMode The delivery mode + * @param priority The priority + * @param timeToLive The time to live + * @exception JMSException Thrown if an error occurs + */ + public void send(final Destination destination, + final Message message, + final int deliveryMode, + final int priority, + final long timeToLive) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + + " destination=" + + destination + + " message=" + + Util.asString(message) + + " deliveryMode=" + + deliveryMode + + " priority=" + + priority + + " ttl=" + + timeToLive); + } + + checkState(); + + _producer.send(destination, message, deliveryMode, priority, timeToLive); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Send message + * @param destination The destination + * @param message The message + * @exception JMSException Thrown if an error occurs + */ + public void send(final Destination destination, final Message message) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + " destination=" + destination + " message=" + Util.asString(message)); + } + + checkState(); + + _producer.send(destination, message); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Send message + * @param message The message + * @param deliveryMode The delivery mode + * @param priority The priority + * @param timeToLive The time to live + * @exception JMSException Thrown if an error occurs + */ + public void send(final Message message, final int deliveryMode, final int priority, final long timeToLive) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + + " message=" + + Util.asString(message) + + " deliveryMode=" + + deliveryMode + + " priority=" + + priority + + " ttl=" + + timeToLive); + } + + checkState(); + + _producer.send(message, deliveryMode, priority, timeToLive); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Send message + * @param message The message + * @exception JMSException Thrown if an error occurs + */ + public void send(final Message message) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + " message=" + Util.asString(message)); + } + + checkState(); + + _producer.send(message); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Get the delivery mode + * @return The mode + * @exception JMSException Thrown if an error occurs + */ + public int getDeliveryMode() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDeliveryMode()"); + } + + return _producer.getDeliveryMode(); + } + + /** + * Get the destination + * @return The destination + * @exception JMSException Thrown if an error occurs + */ + public Destination getDestination() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDestination()"); + } + + return _producer.getDestination(); + } + + /** + * Disable message id + * @return True if disable + * @exception JMSException Thrown if an error occurs + */ + public boolean getDisableMessageID() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDisableMessageID()"); + } + + return _producer.getDisableMessageID(); + } + + /** + * Disable message timestamp + * @return True if disable + * @exception JMSException Thrown if an error occurs + */ + public boolean getDisableMessageTimestamp() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getDisableMessageTimestamp()"); + } + + return _producer.getDisableMessageTimestamp(); + } + + /** + * Get the priority + * @return The priority + * @exception JMSException Thrown if an error occurs + */ + public int getPriority() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getPriority()"); + } + + return _producer.getPriority(); + } + + /** + * Get the time to live + * @return The ttl + * @exception JMSException Thrown if an error occurs + */ + public long getTimeToLive() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTimeToLive()"); + } + + return _producer.getTimeToLive(); + } + + /** + * Set the delivery mode + * @param deliveryMode The mode + * @exception JMSException Thrown if an error occurs + */ + public void setDeliveryMode(final int deliveryMode) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDeliveryMode(" + deliveryMode + ")"); + } + + _producer.setDeliveryMode(deliveryMode); + } + + /** + * Set disable message id + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setDisableMessageID(final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDisableMessageID(" + value + ")"); + } + + _producer.setDisableMessageID(value); + } + + /** + * Set disable message timestamp + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void setDisableMessageTimestamp(final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setDisableMessageTimestamp(" + value + ")"); + } + + _producer.setDisableMessageTimestamp(value); + } + + /** + * Set the priority + * @param defaultPriority The value + * @exception JMSException Thrown if an error occurs + */ + public void setPriority(final int defaultPriority) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setPriority(" + defaultPriority + ")"); + } + + _producer.setPriority(defaultPriority); + } + + /** + * Set the ttl + * @param timeToLive The value + * @exception JMSException Thrown if an error occurs + */ + public void setTimeToLive(final long timeToLive) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setTimeToLive(" + timeToLive + ")"); + } + + _producer.setTimeToLive(timeToLive); + } + + /** + * Check state + * @exception JMSException Thrown if an error occurs + */ + void checkState() throws JMSException + { + _session.checkState(); + } + + /** + * Close producer + * @exception JMSException Thrown if an error occurs + */ + void closeProducer() throws JMSException + { + _producer.close(); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMetaData.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMetaData.java new file mode 100644 index 0000000000..0ad7d089c3 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAMetaData.java @@ -0,0 +1,116 @@ +/* + * + * 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.qpid.ra; + +import javax.resource.ResourceException; +import javax.resource.spi.ManagedConnectionMetaData; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Managed connection meta data + * + */ +public class QpidRAMetaData implements ManagedConnectionMetaData +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAMetaData.class); + + /** The managed connection */ + private final QpidRAManagedConnection _mc; + + /** + * Constructor + * @param mc The managed connection + */ + public QpidRAMetaData(final QpidRAManagedConnection mc) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mc + ")"); + } + + this._mc = mc; + } + + /** + * Get the EIS product name + * @return The name + * @exception ResourceException Thrown if operation fails + */ + public String getEISProductName() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getEISProductName()"); + } + + return "Qpid"; + } + + /** + * Get the EIS product version + * @return The version + * @exception ResourceException Thrown if operation fails + */ + public String getEISProductVersion() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getEISProductVersion()"); + } + + return "2.0"; + } + + /** + * Get the user name + * @return The user name + * @exception ResourceException Thrown if operation fails + */ + public String getUserName() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getUserName()"); + } + + return _mc.getUserName(); + } + + /** + * Get the maximum number of connections -- RETURNS 0 + * @return The number + * @exception ResourceException Thrown if operation fails + */ + public int getMaxConnections() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMaxConnections()"); + } + + return 0; + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAObjectMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAObjectMessage.java new file mode 100644 index 0000000000..926319bb85 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAObjectMessage.java @@ -0,0 +1,85 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; + +import javax.jms.JMSException; +import javax.jms.ObjectMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + * + */ +public class QpidRAObjectMessage extends QpidRAMessage implements ObjectMessage +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAObjectMessage.class); + + /** + * Create a new wrapper + * @param message the message + * @param session the session + */ + public QpidRAObjectMessage(final ObjectMessage message, final QpidRASessionImpl session) + { + super(message, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(message) + ", " + session + ")"); + } + } + + /** + * Get the object + * @return The object + * @exception JMSException Thrown if an error occurs + */ + public Serializable getObject() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getObject()"); + } + + return ((ObjectMessage)_message).getObject(); + } + + /** + * Set the object + * @param object The object + * @exception JMSException Thrown if an error occurs + */ + public void setObject(final Serializable object) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setObject(" + object + ")"); + } + + ((ObjectMessage)_message).setObject(object); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAProperties.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAProperties.java new file mode 100644 index 0000000000..21f7d2574f --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAProperties.java @@ -0,0 +1,186 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The RA default properties - these are set in the ra.xml file + * + */ +public class QpidRAProperties extends ConnectionFactoryProperties implements Serializable +{ + /** Serial version UID */ + private static final long serialVersionUID = -4823893873707374791L; + + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAProperties.class); + + private static final int DEFAULT_SETUP_ATTEMPTS = 10; + + private static final long DEFAULT_SETUP_INTERVAL = 2 * 1000; + + private int _setupAttempts = DEFAULT_SETUP_ATTEMPTS; + + private long _setupInterval = DEFAULT_SETUP_INTERVAL; + + /** Use Local TX instead of XA */ + private Boolean _localTx = false; + + /** Class used to locate the Transaction Manager. */ + private String _transactionManagerLocatorClass ; + + /** Method used to locate the TM */ + private String _transactionManagerLocatorMethod ; + + + /** + * Constructor + */ + public QpidRAProperties() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + } + + /** + * Get the use XA flag + * @return The value + */ + public Boolean getUseLocalTx() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUseLocalTx()"); + } + + return _localTx; + } + + /** + * Set the use XA flag + * @param localTx The value + */ + public void setUseLocalTx(final Boolean localTx) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseLocalTx(" + localTx + ")"); + } + + this._localTx = localTx; + } + + public void setTransactionManagerLocatorClass(final String transactionManagerLocatorClass) + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionManagerLocatorClass(" + transactionManagerLocatorClass + ")"); + } + + this._transactionManagerLocatorClass = transactionManagerLocatorClass; + } + + public String getTransactionManagerLocatorClass() + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionManagerLocatorClass()"); + } + + return _transactionManagerLocatorClass; + } + + public String getTransactionManagerLocatorMethod() + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionManagerLocatorMethod()"); + } + + return _transactionManagerLocatorMethod; + } + + public void setTransactionManagerLocatorMethod(final String transactionManagerLocatorMethod) + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionManagerLocatorMethod(" + transactionManagerLocatorMethod + ")"); + } + + this._transactionManagerLocatorMethod = transactionManagerLocatorMethod; + } + + public int getSetupAttempts() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupAttempts()"); + } + + return _setupAttempts; + } + + public void setSetupAttempts(int setupAttempts) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupAttempts(" + setupAttempts + ")"); + } + + this._setupAttempts = setupAttempts; + } + + public long getSetupInterval() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupInterval()"); + } + + return _setupInterval; + } + + public void setSetupInterval(long setupInterval) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupInterval(" + setupInterval + ")"); + } + + this._setupInterval = setupInterval; + } + + @Override + public String toString() + { + return "QpidRAProperties[localTx=" + _localTx + + ", transactionManagerLocatorClass=" + _transactionManagerLocatorClass + + ", transactionManagerLocatorMethod=" + _transactionManagerLocatorMethod + + ", setupAttempts=" + _setupAttempts + + ", setupInterval=" + _setupInterval + "]"; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueBrowser.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueBrowser.java new file mode 100644 index 0000000000..bcd2d3b63c --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueBrowser.java @@ -0,0 +1,139 @@ +/* + * + * 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.qpid.ra; + +import java.util.Enumeration; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueBrowser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QpidRAQueueBrowser. + * + */ +public class QpidRAQueueBrowser implements QueueBrowser +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAQueueBrowser.class); + + /** The wrapped queue browser */ + protected QueueBrowser _browser; + + /** The session for this consumer */ + protected QpidRASessionImpl _session; + + /** The closed flag */ + private AtomicBoolean _isClosed = new AtomicBoolean(); + + /** + * Create a new wrapper + * @param browser the browser + * @param session the session + */ + public QpidRAQueueBrowser(final QueueBrowser browser, final QpidRASessionImpl session) + { + _browser = browser; + _session = session; + + if (_log.isTraceEnabled()) + { + _log.trace("new QpidRAQueueBrowser " + this + + " browser=" + + Util.asString(browser) + + " session=" + + session); + } + } + + /** + * Close + * @exception JMSException Thrown if an error occurs + */ + public void close() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("close " + this); + } + + if (!_isClosed.getAndSet(true)) + { + try + { + _browser.close(); + } + finally + { + _session.removeQueueBrowser(this); + } + } + } + + /** + * Get the queue associated with this browser. + * @return the associated queue. + */ + public Queue getQueue() + throws JMSException + { + return _browser.getQueue(); + } + + /** + * Get the message selector associated with this browser. + * @return the associated message selector. + */ + public String getMessageSelector() + throws JMSException + { + return _browser.getMessageSelector(); + } + + /** + * Get an enumeration for the current browser. + * @return The enumeration. + */ + public Enumeration getEnumeration() + throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getEnumeration " + this + " browser=" + Util.asString(_browser)); + } + + _session.lock(); + try + { + _session.checkState(); + return _browser.getEnumeration(); + } + finally + { + _session.unlock(); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueReceiver.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueReceiver.java new file mode 100644 index 0000000000..ad1f7fc43b --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueReceiver.java @@ -0,0 +1,70 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueReceiver; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a queue receiver + * + */ +public class QpidRAQueueReceiver extends QpidRAMessageConsumer implements QueueReceiver +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAQueueReceiver.class); + + /** + * Create a new wrapper + * @param consumer the queue receiver + * @param session the session + */ + public QpidRAQueueReceiver(final QueueReceiver consumer, final QpidRASessionImpl session) + { + super(consumer, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(consumer) + ", " + session + ")"); + } + } + + /** + * Get queue + * @return The queue + * @exception JMSException Thrown if an error occurs + */ + public Queue getQueue() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getQueue()"); + } + + checkState(); + return ((QueueReceiver)_consumer).getQueue(); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueSender.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueSender.java new file mode 100644 index 0000000000..7a123618be --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAQueueSender.java @@ -0,0 +1,147 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueSender; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QpidRAQueueSender. + * + */ +public class QpidRAQueueSender extends QpidRAMessageProducer implements QueueSender +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAQueueSender.class); + + /** + * Create a new wrapper + * @param producer the producer + * @param session the session + */ + public QpidRAQueueSender(final QueueSender producer, final QpidRASessionImpl session) + { + super(producer, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(producer) + ", " + session + ")"); + } + } + + /** + * Get queue + * @return The queue + * @exception JMSException Thrown if an error occurs + */ + public Queue getQueue() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getQueue()"); + } + + return ((QueueSender)_producer).getQueue(); + } + + /** + * Send message + * @param destination The destination + * @param message The message + * @param deliveryMode The delivery mode + * @param priority The priority + * @param timeToLive The time to live + * @exception JMSException Thrown if an error occurs + */ + public void send(final Queue destination, + final Message message, + final int deliveryMode, + final int priority, + final long timeToLive) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + + " destination=" + + destination + + " message=" + + Util.asString(message) + + " deliveryMode=" + + deliveryMode + + " priority=" + + priority + + " ttl=" + + timeToLive); + } + + checkState(); + _producer.send(destination, message, deliveryMode, priority, timeToLive); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Send message + * @param destination The destination + * @param message The message + * @exception JMSException Thrown if an error occurs + */ + public void send(final Queue destination, final Message message) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + " destination=" + destination + " message=" + Util.asString(message)); + } + + checkState(); + _producer.send(destination, message); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASession.java index 834b17430b..081677ca4b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/FailoverBeforeConsumingRecoverTest.java +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASession.java @@ -18,23 +18,16 @@ * under the License. * */ -package org.apache.qpid.test.unit.ack; -import org.apache.qpid.jms.Session; +package org.apache.qpid.ra; -import javax.jms.Message; -import javax.jms.Queue; +import javax.jms.JMSException; -public class FailoverBeforeConsumingRecoverTest extends RecoverTest +public interface QpidRASession { + public void setQpidSessionFactory(QpidRASessionFactory sf); - @Override - protected void initTest() throws Exception - { - super.initTest(); - failBroker(getFailingPort()); + public void start() throws JMSException; - Queue queue = _consumerSession.createQueue(getTestQueueName()); - sendMessage(_connection.createSession(false, Session.AUTO_ACKNOWLEDGE), queue, SENT_COUNT); - } + public void close() throws JMSException; } diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactory.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactory.java new file mode 100644 index 0000000000..cf28d5bba1 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactory.java @@ -0,0 +1,62 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TopicConnection; +import javax.jms.XAConnection; +import javax.jms.XAQueueConnection; +import javax.jms.XATopicConnection; + +/** + * A joint interface for all connection types + * + */ +public interface QpidRASessionFactory extends Connection, TopicConnection, QueueConnection, XAConnection, + XATopicConnection, XAQueueConnection +{ + /** Error message for strict behaviour */ + String ISE = "This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6"; + + /** + * Add a temporary queue + * @param temp The temporary queue + */ + void addTemporaryQueue(TemporaryQueue temp); + + /** + * Add a temporary topic + * @param temp The temporary topic + */ + void addTemporaryTopic(TemporaryTopic temp); + + /** + * Notification that a session is closed + * @param session The session + * @throws JMSException for any error + */ + void closeSession(QpidRASessionImpl session) throws JMSException; +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactoryImpl.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactoryImpl.java new file mode 100644 index 0000000000..e2bc2d2008 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionFactoryImpl.java @@ -0,0 +1,911 @@ +/* + * + * 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.qpid.ra; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.Topic; +import javax.jms.TopicSession; +import javax.jms.XAQueueSession; +import javax.jms.XASession; +import javax.jms.XATopicSession; +import javax.naming.Reference; +import javax.resource.Referenceable; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implements the JMS Connection API and produces {@link QpidRASessionImpl} objects. + * + */ +public class QpidRASessionFactoryImpl implements QpidRASessionFactory, Referenceable +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRASessionFactoryImpl.class); + + /** Are we closed? */ + private boolean _closed = false; + + /** The naming reference */ + private Reference _reference; + + /** The user name */ + private String _userName; + + /** The password */ + private String _password; + + /** The client ID */ + private String _clientID; + + /** The connection type */ + private final int _type; + + /** Whether we are started */ + private boolean _started = false; + + /** The managed connection factory */ + private final QpidRAManagedConnectionFactory _mcf; + + /** The connection manager */ + private ConnectionManager _cm; + + /** The sessions */ + private final Set<QpidRASession> _sessions = new HashSet<QpidRASession>(); + + /** The temporary queues */ + private final Set<TemporaryQueue> _tempQueues = new HashSet<TemporaryQueue>(); + + /** The temporary topics */ + private final Set<TemporaryTopic> _tempTopics = new HashSet<TemporaryTopic>(); + + /** + * Constructor + * @param mcf The managed connection factory + * @param cm The connection manager + * @param type The connection type + */ + public QpidRASessionFactoryImpl(final QpidRAManagedConnectionFactory mcf, + final ConnectionManager cm, + final int type) + { + this._mcf = mcf; + + if (cm == null) + { + this._cm = new QpidRAConnectionManager(); + } + else + { + this._cm = cm; + } + + this._type = type; + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mcf + ", " + cm + ", " + type); + } + } + + /** + * Set the naming reference + * @param reference The reference + */ + public void setReference(final Reference reference) + { + if (_log.isTraceEnabled()) + { + _log.trace("setReference(" + reference + ")"); + } + + this._reference = reference; + } + + /** + * Get the naming reference + * @return The reference + */ + public Reference getReference() + { + if (_log.isTraceEnabled()) + { + _log.trace("getReference()"); + } + + return _reference; + } + + /** + * Set the user name + * @param name The user name + */ + public void setUserName(final String name) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUserName(" + name + ")"); + } + + _userName = name; + } + + /** + * Set the password + * @param password The password + */ + public void setPassword(final String password) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPassword(****)"); + } + + this._password = password; + } + + /** + * Get the client ID + * @return The client ID + * @exception JMSException Thrown if an error occurs + */ + public String getClientID() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getClientID()"); + } + + checkClosed(); + + if (_clientID == null) + { + try + { + return _mcf.getDefaultAMQConnectionFactory().getConnectionURL().getClientName() ; + } + catch (final ResourceException re) + { + final JMSException jmse = new JMSException("Unexpected exception obtaining resource") ; + jmse.initCause(re) ; + throw jmse ; + } + } + + return _clientID; + } + + /** + * Set the client ID -- throws IllegalStateException + * @param cID The client ID + * @exception JMSException Thrown if an error occurs + */ + public void setClientID(final String cID) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setClientID(" + cID + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a queue session + * @param transacted Use transactions + * @param acknowledgeMode The acknowledge mode + * @return The queue session + * @exception JMSException Thrown if an error occurs + */ + public QueueSession createQueueSession(final boolean transacted, final int acknowledgeMode) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createQueueSession(" + transacted + ", " + acknowledgeMode + ")"); + } + + checkClosed(); + + if (_type == QpidRAConnectionFactory.TOPIC_CONNECTION || _type == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Can not get a queue session from a topic connection"); + } + + return (QueueSession)allocateConnection(transacted, acknowledgeMode, _type); + } + + /** + * Create a XA queue session + * @return The XA queue session + * @exception JMSException Thrown if an error occurs + */ + public XAQueueSession createXAQueueSession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXAQueueSession()"); + } + + checkClosed(); + + if (_type == QpidRAConnectionFactory.CONNECTION || _type == QpidRAConnectionFactory.TOPIC_CONNECTION || + _type == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Can not get a topic session from a queue connection"); + } + + return (XAQueueSession) allocateConnection(_type); + } + + /** + * Create a connection consumer -- throws IllegalStateException + * @param queue The queue + * @param messageSelector The message selector + * @param sessionPool The session pool + * @param maxMessages The number of max messages + * @return The connection consumer + * @exception JMSException Thrown if an error occurs + */ + public ConnectionConsumer createConnectionConsumer(final Queue queue, + final String messageSelector, + final ServerSessionPool sessionPool, + final int maxMessages) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionConsumer(" + queue + + ", " + + messageSelector + + ", " + + sessionPool + + ", " + + maxMessages + + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a topic session + * @param transacted Use transactions + * @param acknowledgeMode The acknowledge mode + * @return The topic session + * @exception JMSException Thrown if an error occurs + */ + public TopicSession createTopicSession(final boolean transacted, final int acknowledgeMode) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createTopicSession(" + transacted + ", " + acknowledgeMode + ")"); + } + + checkClosed(); + + if (_type == QpidRAConnectionFactory.QUEUE_CONNECTION || _type == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Can not get a topic session from a queue connection"); + } + + return (TopicSession) allocateConnection(transacted, acknowledgeMode, _type); + } + + /** + * Create a XA topic session + * @return The XA topic session + * @exception JMSException Thrown if an error occurs + */ + public XATopicSession createXATopicSession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXATopicSession()"); + } + + checkClosed(); + + if (_type == QpidRAConnectionFactory.CONNECTION || _type == QpidRAConnectionFactory.QUEUE_CONNECTION || + _type == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Can not get a topic session from a queue connection"); + } + + return (XATopicSession) allocateConnection(_type); + } + + /** + * Create a connection consumer -- throws IllegalStateException + * @param topic The topic + * @param messageSelector The message selector + * @param sessionPool The session pool + * @param maxMessages The number of max messages + * @return The connection consumer + * @exception JMSException Thrown if an error occurs + */ + public ConnectionConsumer createConnectionConsumer(final Topic topic, + final String messageSelector, + final ServerSessionPool sessionPool, + final int maxMessages) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionConsumer(" + topic + + ", " + + messageSelector + + ", " + + sessionPool + + ", " + + maxMessages + + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a durable connection consumer -- throws IllegalStateException + * @param topic The topic + * @param subscriptionName The subscription name + * @param messageSelector The message selector + * @param sessionPool The session pool + * @param maxMessages The number of max messages + * @return The connection consumer + * @exception JMSException Thrown if an error occurs + */ + public ConnectionConsumer createDurableConnectionConsumer(final Topic topic, + final String subscriptionName, + final String messageSelector, + final ServerSessionPool sessionPool, + final int maxMessages) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionConsumer(" + topic + + ", " + + subscriptionName + + ", " + + messageSelector + + ", " + + sessionPool + + ", " + + maxMessages + + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a connection consumer -- throws IllegalStateException + * @param destination The destination + * @param pool The session pool + * @param maxMessages The number of max messages + * @return The connection consumer + * @exception JMSException Thrown if an error occurs + */ + public ConnectionConsumer createConnectionConsumer(final Destination destination, + final ServerSessionPool pool, + final int maxMessages) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionConsumer(" + destination + + ", " + + pool + + ", " + + maxMessages + + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a connection consumer -- throws IllegalStateException + * @param destination The destination + * @param name The name + * @param pool The session pool + * @param maxMessages The number of max messages + * @return The connection consumer + * @exception JMSException Thrown if an error occurs + */ + public ConnectionConsumer createConnectionConsumer(final Destination destination, + final String name, + final ServerSessionPool pool, + final int maxMessages) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createConnectionConsumer(" + destination + + ", " + + name + + ", " + + pool + + ", " + + maxMessages + + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Create a session + * @param transacted Use transactions + * @param acknowledgeMode The acknowledge mode + * @return The session + * @exception JMSException Thrown if an error occurs + */ + public Session createSession(final boolean transacted, final int acknowledgeMode) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createSession(" + transacted + ", " + acknowledgeMode + ")"); + } + + checkClosed(); + return (Session) allocateConnection(transacted, acknowledgeMode, _type); + } + + /** + * Create a XA session + * @return The XA session + * @exception JMSException Thrown if an error occurs + */ + public XASession createXASession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("createXASession()"); + } + + checkClosed(); + return (XASession) allocateConnection(_type); + } + + /** + * Get the connection metadata + * @return The connection metadata + * @exception JMSException Thrown if an error occurs + */ + public ConnectionMetaData getMetaData() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMetaData()"); + } + + checkClosed(); + return _mcf.getMetaData(); + } + + /** + * Get the exception listener -- throws IllegalStateException + * @return The exception listener + * @exception JMSException Thrown if an error occurs + */ + public ExceptionListener getExceptionListener() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getExceptionListener()"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Set the exception listener -- throws IllegalStateException + * @param listener The exception listener + * @exception JMSException Thrown if an error occurs + */ + public void setExceptionListener(final ExceptionListener listener) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setExceptionListener(" + listener + ")"); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Start + * @exception JMSException Thrown if an error occurs + */ + public void start() throws JMSException + { + checkClosed(); + + if (_log.isTraceEnabled()) + { + _log.trace("start() " + this); + } + + synchronized (_sessions) + { + if (_started) + { + return; + } + _started = true; + for (Iterator<QpidRASession> i = _sessions.iterator(); i.hasNext();) + { + QpidRASessionImpl session = (QpidRASessionImpl)i.next(); + session.start(); + } + } + } + + /** + * Stop -- throws IllegalStateException + * @exception JMSException Thrown if an error occurs + */ + public void stop() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("stop() " + this); + } + + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + + /** + * Close + * @exception JMSException Thrown if an error occurs + */ + public void close() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("close() " + this); + } + + if (_closed) + { + return; + } + + _closed = true; + + synchronized (_sessions) + { + for (Iterator<QpidRASession> i = _sessions.iterator(); i.hasNext();) + { + QpidRASessionImpl session = (QpidRASessionImpl)i.next(); + try + { + session.closeSession(); + } + catch (Throwable t) + { + _log.trace("Error closing session", t); + } + i.remove(); + } + } + + synchronized (_tempQueues) + { + for (Iterator<TemporaryQueue> i = _tempQueues.iterator(); i.hasNext();) + { + TemporaryQueue temp = (TemporaryQueue)i.next(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("Closing temporary queue " + temp + " for " + this); + } + temp.delete(); + } + catch (Throwable t) + { + _log.trace("Error deleting temporary queue", t); + } + i.remove(); + } + } + + synchronized (_tempTopics) + { + for (Iterator<TemporaryTopic> i = _tempTopics.iterator(); i.hasNext();) + { + TemporaryTopic temp = (TemporaryTopic)i.next(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("Closing temporary topic " + temp + " for " + this); + } + temp.delete(); + } + catch (Throwable t) + { + _log.trace("Error deleting temporary queue", t); + } + i.remove(); + } + } + } + + /** + * Close session + * @param session The session + * @exception JMSException Thrown if an error occurs + */ + public void closeSession(final QpidRASessionImpl session) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("closeSession(" + session + ")"); + } + + synchronized (_sessions) + { + _sessions.remove(session); + } + } + + /** + * Add temporary queue + * @param temp The temporary queue + */ + public void addTemporaryQueue(final TemporaryQueue temp) + { + if (_log.isTraceEnabled()) + { + _log.trace("addTemporaryQueue(" + temp + ")"); + } + + synchronized (_tempQueues) + { + _tempQueues.add(temp); + } + } + + /** + * Add temporary topic + * @param temp The temporary topic + */ + public void addTemporaryTopic(final TemporaryTopic temp) + { + if (_log.isTraceEnabled()) + { + _log.trace("addTemporaryTopic(" + temp + ")"); + } + + synchronized (_tempTopics) + { + _tempTopics.add(temp); + } + } + + /** + * Allocation a connection + * @param sessionType The session type + * @return The session + * @exception JMSException Thrown if an error occurs + */ + protected QpidRASession allocateConnection(final int sessionType) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("allocateConnection(" + sessionType + ")"); + } + + try + { + synchronized (_sessions) + { + if (_sessions.isEmpty() == false) + { + throw new IllegalStateException("Only allowed one session per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6"); + } + + QpidRAConnectionRequestInfo info = new QpidRAConnectionRequestInfo(sessionType); + info.setUserName(_userName); + info.setPassword(_password); + info.setClientID(_clientID); + info.setDefaults(_mcf.getDefaultAMQConnectionFactory().getConnectionURL()); + + if (_log.isTraceEnabled()) + { + _log.trace("Allocating session for " + this + " with request info=" + info); + } + + QpidRASession session = (QpidRASession)_cm.allocateConnection(_mcf, info); + + try + { + if (_log.isTraceEnabled()) + { + _log.trace("Allocated " + this + " session=" + session); + } + + session.setQpidSessionFactory(this); + + if (_started) + { + session.start(); + } + + _sessions.add(session); + + return session; + } + catch (Throwable t) + { + try + { + session.close(); + } + catch (Throwable ignored) + { + } + if (t instanceof Exception) + { + throw (Exception)t; + } + else + { + throw new RuntimeException("Unexpected error: ", t); + } + } + } + } + catch (Exception e) + { + _log.error("Could not create session", e); + + JMSException je = new JMSException("Could not create a session: " + e.getMessage()); + je.setLinkedException(e); + throw je; + } + } + + /** + * Allocation a connection + * @param transacted Use transactions + * @param acknowledgeMode The acknowledge mode + * @param sessionType The session type + * @return The session + * @exception JMSException Thrown if an error occurs + */ + protected QpidRASession allocateConnection(final boolean transacted, int acknowledgeMode, final int sessionType) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("allocateConnection(" + transacted + + ", " + + acknowledgeMode + + ", " + + sessionType + + ")"); + } + + try + { + synchronized (_sessions) + { + if (_sessions.isEmpty() == false) + { + throw new IllegalStateException("Only allowed one session per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6"); + } + + if (transacted) + { + acknowledgeMode = Session.SESSION_TRANSACTED; + } + + QpidRAConnectionRequestInfo info = new QpidRAConnectionRequestInfo(transacted, + acknowledgeMode, + sessionType); + info.setUserName(_userName); + info.setPassword(_password); + info.setClientID(_clientID); + info.setDefaults(_mcf.getDefaultAMQConnectionFactory().getConnectionURL()); + + if (_log.isTraceEnabled()) + { + _log.trace("Allocating session for " + this + " with request info=" + info); + } + + QpidRASession session = (QpidRASession)_cm.allocateConnection(_mcf, info); + + try + { + if (_log.isTraceEnabled()) + { + _log.trace("Allocated " + this + " session=" + session); + } + + session.setQpidSessionFactory(this); + + if (_started) + { + session.start(); + } + + _sessions.add(session); + + return session; + } + catch (Throwable t) + { + try + { + session.close(); + } + catch (Throwable ignored) + { + } + if (t instanceof Exception) + { + throw (Exception)t; + } + else + { + throw new RuntimeException("Unexpected error: ", t); + } + } + } + } + catch (Exception e) + { + _log.error("Could not create session", e); + + JMSException je = new JMSException("Could not create a session: " + e.getMessage()); + je.setLinkedException(e); + throw je; + } + } + + /** + * Check if we are closed + * @exception IllegalStateException Thrown if closed + */ + protected void checkClosed() throws IllegalStateException + { + if (_closed) + { + throw new IllegalStateException("The connection is closed"); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionImpl.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionImpl.java new file mode 100644 index 0000000000..a270253c13 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRASessionImpl.java @@ -0,0 +1,1732 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; +import javax.jms.TransactionInProgressException; +import javax.jms.XAQueueSession; +import javax.jms.XASession; +import javax.jms.XATopicSession; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ManagedConnection; +import javax.transaction.xa.XAResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A joint interface for JMS sessions + * + */ +public class QpidRASessionImpl implements Session, QueueSession, TopicSession, XASession, XAQueueSession, XATopicSession, QpidRASession +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRASessionImpl.class); + + /** The managed connection */ + private volatile QpidRAManagedConnection _mc; + /** The locked managed connection */ + private QpidRAManagedConnection _lockedMC; + + /** The connection request info */ + private final QpidRAConnectionRequestInfo _cri; + + /** The session factory */ + private QpidRASessionFactory _sf; + + /** The message consumers */ + private final Set<MessageConsumer> _consumers; + + /** The message producers */ + private final Set<MessageProducer> _producers; + + /** The queue browsers */ + private final Set<QueueBrowser> _browsers; + + /** Are we started */ + private AtomicBoolean _started = new AtomicBoolean(false) ; + + /** + * Constructor + * @param mc The managed connection + * @param cri The connection request info + */ + public QpidRASessionImpl(final QpidRAManagedConnection mc, final QpidRAConnectionRequestInfo cri) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + mc + ", " + cri + ")"); + } + + this._mc = mc; + this._cri = cri; + _sf = null; + _consumers = new HashSet<MessageConsumer>(); + _producers = new HashSet<MessageProducer>(); + _browsers = new HashSet<QueueBrowser>(); + } + + /** + * Set the session factory + * @param sf The session factory + */ + public void setQpidSessionFactory(final QpidRASessionFactory sf) + { + if (_log.isTraceEnabled()) + { + _log.trace("setQpidSessionFactory(" + sf + ")"); + } + + _started.set(false) ; + this._sf = sf; + } + + /** + * Lock + * @exception JMSException Thrown if an error occurs + * @exception IllegalStateException The session is closed + */ + protected void lock() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("lock()"); + } + + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + mc.tryLock(); + _lockedMC = mc ; + } + else + { + throw new IllegalStateException("Connection is not associated with a managed connection. " + this); + } + } + + /** + * Unlock + */ + protected void unlock() + { + if (_log.isTraceEnabled()) + { + _log.trace("unlock()"); + } + + if (_lockedMC != null) + { + try + { + _lockedMC.unlock(); + } + finally + { + _lockedMC = null ; + } + } + + // We recreate the lock when returned to the pool + // so missing the unlock after disassociation is not important + } + + /** + * Create a bytes message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public BytesMessage createBytesMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createBytesMessage" + Util.asString(session)); + } + + return session.createBytesMessage(); + } + + /** + * Create a map message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public MapMessage createMapMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createMapMessage" + Util.asString(session)); + } + + return session.createMapMessage(); + } + + /** + * Create a message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public Message createMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createMessage" + Util.asString(session)); + } + + return session.createMessage(); + } + + /** + * Create an object message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public ObjectMessage createObjectMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createObjectMessage" + Util.asString(session)); + } + + return session.createObjectMessage(); + } + + /** + * Create an object message + * @param object The object + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public ObjectMessage createObjectMessage(final Serializable object) throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createObjectMessage(" + object + ")" + Util.asString(session)); + } + + return session.createObjectMessage(object); + } + + /** + * Create a stream message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public StreamMessage createStreamMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createStreamMessage" + Util.asString(session)); + } + + return session.createStreamMessage(); + } + + /** + * Create a text message + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public TextMessage createTextMessage() throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createTextMessage" + Util.asString(session)); + } + + return session.createTextMessage(); + } + + /** + * Create a text message + * @param string The text + * @return The message + * @exception JMSException Thrown if an error occurs + */ + public TextMessage createTextMessage(final String string) throws JMSException + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createTextMessage(" + string + ")" + Util.asString(session)); + } + + return session.createTextMessage(string); + } + + /** + * Get transacted + * @return True if transacted; otherwise false + * @exception JMSException Thrown if an error occurs + */ + public boolean getTransacted() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransacted()"); + } + + getSessionInternal(); + return _cri.isTransacted(); + } + + /** + * Get the message listener -- throws IllegalStateException + * @return The message listener + * @exception JMSException Thrown if an error occurs + */ + public MessageListener getMessageListener() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageListener()"); + } + + throw new IllegalStateException("Method not allowed"); + } + + /** + * Set the message listener -- Throws IllegalStateException + * @param listener The message listener + * @exception JMSException Thrown if an error occurs + */ + public void setMessageListener(final MessageListener listener) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setMessageListener(" + listener + ")"); + } + + throw new IllegalStateException("Method not allowed"); + } + + /** + * Always throws an Error. + * @exception Error Method not allowed. + */ + public void run() + { + if (_log.isTraceEnabled()) + { + _log.trace("run()"); + } + + throw new Error("Method not allowed"); + } + + /** + * Closes the session. Sends a ConnectionEvent.CONNECTION_CLOSED to the + * managed connection. + * @exception JMSException Failed to close session. + */ + public void close() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("close()"); + } + + _sf.closeSession(this); + closeSession(); + } + + /** + * Commit + * @exception JMSException Failed to close session. + */ + public void commit() throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.XA_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new TransactionInProgressException("XA connection"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_cri.isTransacted() == false) + { + throw new IllegalStateException("Session is not transacted"); + } + + if (_log.isTraceEnabled()) + { + _log.trace("Commit session " + this); + } + + session.commit(); + } + finally + { + unlock(); + } + } + + /** + * Rollback + * @exception JMSException Failed to close session. + */ + public void rollback() throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.XA_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new TransactionInProgressException("XA connection"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_cri.isTransacted() == false) + { + throw new IllegalStateException("Session is not transacted"); + } + + if (_log.isTraceEnabled()) + { + _log.trace("Rollback session " + this); + } + + session.rollback(); + } + finally + { + unlock(); + } + } + + /** + * Recover + * @exception JMSException Failed to close session. + */ + public void recover() throws JMSException + { + lock(); + try + { + Session session = getSessionInternal(); + + if (_cri.isTransacted()) + { + throw new IllegalStateException("Session is transacted"); + } + + if (_log.isTraceEnabled()) + { + _log.trace("Recover session " + this); + } + + session.recover(); + } + finally + { + unlock(); + } + } + + /** + * Create a topic + * @param topicName The topic name + * @return The topic + * @exception JMSException Thrown if an error occurs + */ + public Topic createTopic(final String topicName) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Cannot create topic for javax.jms.QueueSession"); + } + + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createTopic " + Util.asString(session) + " topicName=" + topicName); + } + + Topic result = session.createTopic(topicName); + + if (_log.isTraceEnabled()) + { + _log.trace("createdTopic " + Util.asString(session) + " topic=" + result); + } + + return result; + } + + /** + * Create a topic subscriber + * @param topic The topic + * @return The subscriber + * @exception JMSException Thrown if an error occurs + */ + public TopicSubscriber createSubscriber(final Topic topic) throws JMSException + { + lock(); + try + { + TopicSession session = getTopicSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createSubscriber " + Util.asString(session) + " topic=" + topic); + } + + TopicSubscriber result = session.createSubscriber(topic); + result = new QpidRATopicSubscriber(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdSubscriber " + Util.asString(session) + " QpidRATopicSubscriber=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a topic subscriber + * @param topic The topic + * @param messageSelector The message selector + * @param noLocal If true inhibits the delivery of messages published by its own connection + * @return The subscriber + * @exception JMSException Thrown if an error occurs + */ + public TopicSubscriber createSubscriber(final Topic topic, final String messageSelector, final boolean noLocal) throws JMSException + { + lock(); + try + { + TopicSession session = getTopicSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createSubscriber " + Util.asString(session) + + " topic=" + + topic + + " selector=" + + messageSelector + + " noLocal=" + + noLocal); + } + + TopicSubscriber result = session.createSubscriber(topic, messageSelector, noLocal); + result = new QpidRATopicSubscriber(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdSubscriber " + Util.asString(session) + " QpidRATopicSubscriber=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a durable topic subscriber + * @param topic The topic + * @param name The name + * @return The subscriber + * @exception JMSException Thrown if an error occurs + */ + public TopicSubscriber createDurableSubscriber(final Topic topic, final String name) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Cannot create durable subscriber from javax.jms.QueueSession"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createDurableSubscriber " + Util.asString(session) + " topic=" + topic + " name=" + name); + } + + TopicSubscriber result = session.createDurableSubscriber(topic, name); + result = new QpidRATopicSubscriber(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdDurableSubscriber " + Util.asString(session) + " QpidRATopicSubscriber=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a topic subscriber + * @param topic The topic + * @param name The name + * @param messageSelector The message selector + * @param noLocal If true inhibits the delivery of messages published by its own connection + * @return The subscriber + * @exception JMSException Thrown if an error occurs + */ + public TopicSubscriber createDurableSubscriber(final Topic topic, + final String name, + final String messageSelector, + final boolean noLocal) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Cannot create durable subscriber from javax.jms.QueueSession"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createDurableSubscriber " + Util.asString(session) + + " topic=" + + topic + + " name=" + + name + + " selector=" + + messageSelector + + " noLocal=" + + noLocal); + } + + TopicSubscriber result = session.createDurableSubscriber(topic, name, messageSelector, noLocal); + result = new QpidRATopicSubscriber(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdDurableSubscriber " + Util.asString(session) + " QpidRATopicSubscriber=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a topic publisher + * @param topic The topic + * @return The publisher + * @exception JMSException Thrown if an error occurs + */ + public TopicPublisher createPublisher(final Topic topic) throws JMSException + { + lock(); + try + { + TopicSession session = getTopicSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createPublisher " + Util.asString(session) + " topic=" + topic); + } + + TopicPublisher result = session.createPublisher(topic); + result = new QpidRATopicPublisher(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdPublisher " + Util.asString(session) + " publisher=" + result); + } + + addProducer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a temporary topic + * @return The temporary topic + * @exception JMSException Thrown if an error occurs + */ + public TemporaryTopic createTemporaryTopic() throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Cannot create temporary topic for javax.jms.QueueSession"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createTemporaryTopic " + Util.asString(session)); + } + + TemporaryTopic temp = session.createTemporaryTopic(); + + if (_log.isTraceEnabled()) + { + _log.trace("createdTemporaryTopic " + Util.asString(session) + " temp=" + temp); + } + + _sf.addTemporaryTopic(temp); + + return temp; + } + finally + { + unlock(); + } + } + + /** + * Unsubscribe + * @param name The name + * @exception JMSException Thrown if an error occurs + */ + public void unsubscribe(final String name) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_QUEUE_CONNECTION) + { + throw new IllegalStateException("Cannot unsubscribe for javax.jms.QueueSession"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("unsubscribe " + Util.asString(session) + " name=" + name); + } + + session.unsubscribe(name); + } + finally + { + unlock(); + } + } + + /** + * Create a browser + * @param queue The queue + * @return The browser + * @exception JMSException Thrown if an error occurs + */ + public QueueBrowser createBrowser(final Queue queue) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Cannot create browser for javax.jms.TopicSession"); + } + + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createBrowser " + Util.asString(session) + " queue=" + queue); + } + + QueueBrowser result = session.createBrowser(queue); + + if (_log.isTraceEnabled()) + { + _log.trace("createdBrowser " + Util.asString(session) + " browser=" + result); + } + + return result; + } + + /** + * Create a browser + * @param queue The queue + * @param messageSelector The message selector + * @return The browser + * @exception JMSException Thrown if an error occurs + */ + public QueueBrowser createBrowser(final Queue queue, final String messageSelector) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Cannot create browser for javax.jms.TopicSession"); + } + + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createBrowser " + Util.asString(session) + " queue=" + queue + " selector=" + messageSelector); + } + + QueueBrowser result = session.createBrowser(queue, messageSelector); + result = new QpidRAQueueBrowser(result, this); + addQueueBrowser(result) ; + + if (_log.isTraceEnabled()) + { + _log.trace("createdBrowser " + Util.asString(session) + " browser=" + result); + } + + return result; + } + + /** + * Create a queue + * @param queueName The queue name + * @return The queue + * @exception JMSException Thrown if an error occurs + */ + public Queue createQueue(final String queueName) throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Cannot create browser or javax.jms.TopicSession"); + } + + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createQueue " + Util.asString(session) + " queueName=" + queueName); + } + + Queue result = session.createQueue(queueName); + + if (_log.isTraceEnabled()) + { + _log.trace("createdQueue " + Util.asString(session) + " queue=" + result); + } + + return result; + } + + /** + * Create a queue receiver + * @param queue The queue + * @return The queue receiver + * @exception JMSException Thrown if an error occurs + */ + public QueueReceiver createReceiver(final Queue queue) throws JMSException + { + lock(); + try + { + QueueSession session = getQueueSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createReceiver " + Util.asString(session) + " queue=" + queue); + } + + QueueReceiver result = session.createReceiver(queue); + result = new QpidRAQueueReceiver(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdReceiver " + Util.asString(session) + " receiver=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a queue receiver + * @param queue The queue + * @param messageSelector + * @return The queue receiver + * @exception JMSException Thrown if an error occurs + */ + public QueueReceiver createReceiver(final Queue queue, final String messageSelector) throws JMSException + { + lock(); + try + { + QueueSession session = getQueueSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createReceiver " + Util.asString(session) + " queue=" + queue + " selector=" + messageSelector); + } + + QueueReceiver result = session.createReceiver(queue, messageSelector); + result = new QpidRAQueueReceiver(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdReceiver " + Util.asString(session) + " receiver=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a queue sender + * @param queue The queue + * @return The queue sender + * @exception JMSException Thrown if an error occurs + */ + public QueueSender createSender(final Queue queue) throws JMSException + { + lock(); + try + { + QueueSession session = getQueueSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createSender " + Util.asString(session) + " queue=" + queue); + } + + QueueSender result = session.createSender(queue); + result = new QpidRAQueueSender(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdSender " + Util.asString(session) + " sender=" + result); + } + + addProducer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a temporary queue + * @return The temporary queue + * @exception JMSException Thrown if an error occurs + */ + public TemporaryQueue createTemporaryQueue() throws JMSException + { + if (_cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION || _cri.getType() == QpidRAConnectionFactory.XA_TOPIC_CONNECTION) + { + throw new IllegalStateException("Cannot create temporary queue for javax.jms.TopicSession"); + } + + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createTemporaryQueue " + Util.asString(session)); + } + + TemporaryQueue temp = session.createTemporaryQueue(); + + if (_log.isTraceEnabled()) + { + _log.trace("createdTemporaryQueue " + Util.asString(session) + " temp=" + temp); + } + + _sf.addTemporaryQueue(temp); + + return temp; + } + finally + { + unlock(); + } + } + + /** + * Create a message consumer + * @param destination The destination + * @return The message consumer + * @exception JMSException Thrown if an error occurs + */ + public MessageConsumer createConsumer(final Destination destination) throws JMSException + { + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createConsumer " + Util.asString(session) + " dest=" + destination); + } + + MessageConsumer result = session.createConsumer(destination); + result = new QpidRAMessageConsumer(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdConsumer " + Util.asString(session) + " consumer=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a message consumer + * @param destination The destination + * @param messageSelector The message selector + * @return The message consumer + * @exception JMSException Thrown if an error occurs + */ + public MessageConsumer createConsumer(final Destination destination, final String messageSelector) throws JMSException + { + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createConsumer " + Util.asString(session) + + " dest=" + + destination + + " messageSelector=" + + messageSelector); + } + + MessageConsumer result = session.createConsumer(destination, messageSelector); + result = new QpidRAMessageConsumer(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdConsumer " + Util.asString(session) + " consumer=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a message consumer + * @param destination The destination + * @param messageSelector The message selector + * @param noLocal If true inhibits the delivery of messages published by its own connection + * @return The message consumer + * @exception JMSException Thrown if an error occurs + */ + public MessageConsumer createConsumer(final Destination destination, + final String messageSelector, + final boolean noLocal) throws JMSException + { + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createConsumer " + Util.asString(session) + + " dest=" + + destination + + " messageSelector=" + + messageSelector + + " noLocal=" + + noLocal); + } + + MessageConsumer result = session.createConsumer(destination, messageSelector, noLocal); + result = new QpidRAMessageConsumer(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdConsumer " + Util.asString(session) + " consumer=" + result); + } + + addConsumer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Create a message producer + * @param destination The destination + * @return The message producer + * @exception JMSException Thrown if an error occurs + */ + public MessageProducer createProducer(final Destination destination) throws JMSException + { + lock(); + try + { + Session session = getSessionInternal(); + + if (_log.isTraceEnabled()) + { + _log.trace("createProducer " + Util.asString(session) + " dest=" + destination); + } + + MessageProducer result = session.createProducer(destination); + result = new QpidRAMessageProducer(result, this); + + if (_log.isTraceEnabled()) + { + _log.trace("createdProducer " + Util.asString(session) + " producer=" + result); + } + + addProducer(result); + + return result; + } + finally + { + unlock(); + } + } + + /** + * Get the acknowledge mode + * @return The mode + * @exception JMSException Thrown if an error occurs + */ + public int getAcknowledgeMode() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getAcknowledgeMode()"); + } + + getSessionInternal(); + return _cri.getAcknowledgeMode(); + } + + /** + * Get the XA resource + * @return The XA resource + * @exception IllegalStateException If non XA connection + */ + public XAResource getXAResource() + { + if (_log.isTraceEnabled()) + { + _log.trace("getXAResource()"); + } + + if (_cri.getType() == QpidRAConnectionFactory.CONNECTION || _cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION) + { + return null; + } + + try + { + lock(); + + return getXAResourceInternal(); + } + catch (Throwable t) + { + return null; + } + finally + { + unlock(); + } + } + + /** + * Get the session + * @return The session + * @exception JMSException Thrown if an error occurs + */ + public Session getSession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getSession()"); + } + + if (_cri.getType() == QpidRAConnectionFactory.CONNECTION || _cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION) + { + throw new IllegalStateException("Non XA connection"); + } + + lock(); + try + { + return this; + } + finally + { + unlock(); + } + } + + /** + * Get the queue session + * @return The queue session + * @exception JMSException Thrown if an error occurs + */ + public QueueSession getQueueSession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getQueueSession()"); + } + + if (_cri.getType() == QpidRAConnectionFactory.CONNECTION || _cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION) + { + throw new IllegalStateException("Non XA connection"); + } + + lock(); + try + { + return this; + } + finally + { + unlock(); + } + } + + /** + * Get the topic session + * @return The topic session + * @exception JMSException Thrown if an error occurs + */ + public TopicSession getTopicSession() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTopicSession()"); + } + + if (_cri.getType() == QpidRAConnectionFactory.CONNECTION || _cri.getType() == QpidRAConnectionFactory.QUEUE_CONNECTION || + _cri.getType() == QpidRAConnectionFactory.TOPIC_CONNECTION) + { + throw new IllegalStateException("Non XA connection"); + } + + lock(); + try + { + return this; + } + finally + { + unlock(); + } + } + + /** + * Set the managed connection + * @param managedConnection The managed connection + */ + void setManagedConnection(final QpidRAManagedConnection managedConnection) + { + if (_log.isTraceEnabled()) + { + _log.trace("setManagedConnection(" + managedConnection + ")"); + } + + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + mc.removeHandle(this); + } + + this._mc = managedConnection; + } + + /** for tests only */ + public ManagedConnection getManagedConnection() + { + return _mc; + } + + /** + * Destroy + */ + void destroy() + { + if (_log.isTraceEnabled()) + { + _log.trace("destroy()"); + } + + _mc = null; + } + + /** + * Start + * @exception JMSException Thrown if an error occurs + */ + public void start() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("start()"); + } + + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + _started.set(true) ; + mc.start(); + } + } + + /** + * Stop + * @exception JMSException Thrown if an error occurs + */ + void stop() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("stop()"); + } + + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + mc.stop(); + _started.set(false) ; + } + } + + /** + * Check strict + * @exception JMSException Thrown if an error occurs + */ + void checkStrict() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("checkStrict()"); + } + + if (_mc != null) + { + throw new IllegalStateException(QpidRASessionFactory.ISE); + } + } + + /** + * Close session + * @exception JMSException Thrown if an error occurs + */ + void closeSession() throws JMSException + { + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + _log.trace("Closing session"); + + try + { + mc.stop(); + } + catch (Throwable t) + { + _log.trace("Error stopping managed connection", t); + } + + synchronized (_consumers) + { + for (Iterator<MessageConsumer> i = _consumers.iterator(); i.hasNext();) + { + QpidRAMessageConsumer consumer = (QpidRAMessageConsumer)i.next(); + try + { + consumer.closeConsumer(); + } + catch (Throwable t) + { + _log.trace("Error closing consumer", t); + } + i.remove(); + } + } + + synchronized (_producers) + { + for (Iterator<MessageProducer> i = _producers.iterator(); i.hasNext();) + { + QpidRAMessageProducer producer = (QpidRAMessageProducer)i.next(); + try + { + producer.closeProducer(); + } + catch (Throwable t) + { + _log.trace("Error closing producer", t); + } + i.remove(); + } + } + + synchronized (_browsers) + { + for (Iterator<QueueBrowser> i = _browsers.iterator(); i.hasNext();) + { + QpidRAQueueBrowser browser = (QpidRAQueueBrowser)i.next(); + try + { + browser.close(); + } + catch (Throwable t) + { + _log.trace("Error closing browser", t); + } + i.remove(); + } + } + + mc.removeHandle(this); + ConnectionEvent ev = new ConnectionEvent(mc, ConnectionEvent.CONNECTION_CLOSED); + ev.setConnectionHandle(this); + mc.sendEvent(ev); + this._mc = null; + } + } + + /** + * Add consumer + * @param consumer The consumer + */ + void addConsumer(final MessageConsumer consumer) + { + if (_log.isTraceEnabled()) + { + _log.trace("addConsumer(" + consumer + ")"); + } + + synchronized (_consumers) + { + _consumers.add(consumer); + } + } + + /** + * Remove consumer + * @param consumer The consumer + */ + void removeConsumer(final MessageConsumer consumer) + { + if (_log.isTraceEnabled()) + { + _log.trace("removeConsumer(" + consumer + ")"); + } + + synchronized (_consumers) + { + _consumers.remove(consumer); + } + } + + /** + * Add producer + * @param producer The producer + */ + void addProducer(final MessageProducer producer) + { + if (_log.isTraceEnabled()) + { + _log.trace("addProducer(" + producer + ")"); + } + + synchronized (_producers) + { + _producers.add(producer); + } + } + + /** + * Remove producer + * @param producer The producer + */ + void removeProducer(final MessageProducer producer) + { + if (_log.isTraceEnabled()) + { + _log.trace("removeProducer(" + producer + ")"); + } + + synchronized (_producers) + { + _producers.remove(producer); + } + } + + /** + * Add queue browser + * @param browser The queue browser + */ + void addQueueBrowser(final QueueBrowser browser) + { + if (_log.isTraceEnabled()) + { + _log.trace("addQueueBrowser(" + browser + ")"); + } + + synchronized (_browsers) + { + _browsers.add(browser); + } + } + + /** + * Remove queue browser + * @param browser The queue browser + */ + void removeQueueBrowser(final QueueBrowser browser) + { + if (_log.isTraceEnabled()) + { + _log.trace("removeQueueBrowser(" + browser + ")"); + } + + synchronized (_browsers) + { + _browsers.remove(browser); + } + } + + /** + * Get the session and ensure that it is open + * @return The session + * @exception JMSException Thrown if an error occurs + * @exception IllegalStateException The session is closed + */ + Session getSessionInternal() throws JMSException + { + final QpidRAManagedConnection mc = this._mc; + if (mc == null) + { + throw new IllegalStateException("The session is closed"); + } + + Session session = mc.getSession(); + + if (_log.isTraceEnabled()) + { + _log.trace("getSessionInternal " + Util.asString(session) + " for " + this); + } + + return session; + } + + /** + * Get the XA resource and ensure that it is open + * @return The XA Resource + * @exception JMSException Thrown if an error occurs + * @exception IllegalStateException The session is closed + */ + XAResource getXAResourceInternal() throws JMSException + { + final QpidRAManagedConnection mc = this._mc; + if (mc == null) + { + throw new IllegalStateException("The session is closed"); + } + + try + { + XAResource xares = mc.getXAResource(); + + if (_log.isTraceEnabled()) + { + _log.trace("getXAResourceInternal " + xares + " for " + this); + } + + return xares; + } + catch (ResourceException e) + { + JMSException jmse = new JMSException("Unable to get XA Resource"); + jmse.initCause(e); + throw jmse; + } + } + + /** + * Get the queue session + * @return The queue session + * @exception JMSException Thrown if an error occurs + * @exception IllegalStateException The session is closed + */ + QueueSession getQueueSessionInternal() throws JMSException + { + Session s = getSessionInternal(); + if (!(s instanceof QueueSession)) + { + throw new InvalidDestinationException("Attempting to use QueueSession methods on: " + this); + } + return (QueueSession)s; + } + + /** + * Get the topic session + * @return The topic session + * @exception JMSException Thrown if an error occurs + * @exception IllegalStateException The session is closed + */ + TopicSession getTopicSessionInternal() throws JMSException + { + Session s = getSessionInternal(); + if (!(s instanceof TopicSession)) + { + throw new InvalidDestinationException("Attempting to use TopicSession methods on: " + this); + } + return (TopicSession)s; + } + + /** + * @throws SystemException + * @throws RollbackException + * + */ + public void checkState() throws JMSException + { + final QpidRAManagedConnection mc = this._mc; + if (mc != null) + { + mc.checkTransactionActive(); + } + } + + /** + * Has this session been started? + * @return true if started, false if stopped. + */ + public boolean isStarted() + { + return _started.get(); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAStreamMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAStreamMessage.java new file mode 100644 index 0000000000..3c2bee33f2 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAStreamMessage.java @@ -0,0 +1,415 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.StreamMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + * + */ +public class QpidRAStreamMessage extends QpidRAMessage implements StreamMessage +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAStreamMessage.class); + + /** + * Create a new wrapper + * @param message the message + * @param session the session + */ + public QpidRAStreamMessage(final StreamMessage message, final QpidRASessionImpl session) + { + super(message, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(message) + ", " + session + ")"); + } + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean readBoolean() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readBoolean()"); + } + + return ((StreamMessage)_message).readBoolean(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public byte readByte() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readByte()"); + } + + return ((StreamMessage)_message).readByte(); + } + + /** + * Read + * @param value The value + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int readBytes(final byte[] value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readBytes(" + value + ")"); + } + + return ((StreamMessage)_message).readBytes(value); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public char readChar() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readChar()"); + } + + return ((StreamMessage)_message).readChar(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public double readDouble() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readDouble()"); + } + + return ((StreamMessage)_message).readDouble(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public float readFloat() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readFloat()"); + } + + return ((StreamMessage)_message).readFloat(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public int readInt() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readInt()"); + } + + return ((StreamMessage)_message).readInt(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public long readLong() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readLong()"); + } + + return ((StreamMessage)_message).readLong(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public Object readObject() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readObject()"); + } + + return ((StreamMessage)_message).readObject(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public short readShort() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readShort()"); + } + + return ((StreamMessage)_message).readShort(); + } + + /** + * Read + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public String readString() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("readString()"); + } + + return ((StreamMessage)_message).readString(); + } + + /** + * Reset + * @exception JMSException Thrown if an error occurs + */ + public void reset() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("reset()"); + } + + ((StreamMessage)_message).reset(); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeBoolean(final boolean value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBoolean(" + value + ")"); + } + + ((StreamMessage)_message).writeBoolean(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeByte(final byte value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeByte(" + value + ")"); + } + + ((StreamMessage)_message).writeByte(value); + } + + /** + * Write + * @param value The value + * @param offset The offset + * @param length The length + * @exception JMSException Thrown if an error occurs + */ + public void writeBytes(final byte[] value, final int offset, final int length) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBytes(" + value + ", " + offset + ", " + length + ")"); + } + + ((StreamMessage)_message).writeBytes(value, offset, length); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeBytes(final byte[] value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeBytes(" + value + ")"); + } + + ((StreamMessage)_message).writeBytes(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeChar(final char value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeChar(" + value + ")"); + } + + ((StreamMessage)_message).writeChar(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeDouble(final double value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeDouble(" + value + ")"); + } + + ((StreamMessage)_message).writeDouble(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeFloat(final float value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeFloat(" + value + ")"); + } + + ((StreamMessage)_message).writeFloat(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeInt(final int value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeInt(" + value + ")"); + } + + ((StreamMessage)_message).writeInt(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeLong(final long value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeLong(" + value + ")"); + } + + ((StreamMessage)_message).writeLong(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeObject(final Object value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeObject(" + value + ")"); + } + + ((StreamMessage)_message).writeObject(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeShort(final short value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeShort(" + value + ")"); + } + + ((StreamMessage)_message).writeShort(value); + } + + /** + * Write + * @param value The value + * @exception JMSException Thrown if an error occurs + */ + public void writeString(final String value) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("writeString(" + value + ")"); + } + + ((StreamMessage)_message).writeString(value); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATextMessage.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATextMessage.java new file mode 100644 index 0000000000..d74d006a7f --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATextMessage.java @@ -0,0 +1,83 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.TextMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a message + * + */ +public class QpidRATextMessage extends QpidRAMessage implements TextMessage +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRATextMessage.class); + + /** + * Create a new wrapper + * @param message the message + * @param session the session + */ + public QpidRATextMessage(final TextMessage message, final QpidRASessionImpl session) + { + super(message, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(message) + ", " + session + ")"); + } + } + + /** + * Get text + * @return The text + * @exception JMSException Thrown if an error occurs + */ + public String getText() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getText()"); + } + + return ((TextMessage)_message).getText(); + } + + /** + * Set text + * @param string The text + * @exception JMSException Thrown if an error occurs + */ + public void setText(final String string) throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("setText(" + string + ")"); + } + + ((TextMessage)_message).setText(string); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicPublisher.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicPublisher.java new file mode 100644 index 0000000000..b753690142 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicPublisher.java @@ -0,0 +1,220 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Topic; +import javax.jms.TopicPublisher; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QpidRATopicPublisher. + * + */ +public class QpidRATopicPublisher extends QpidRAMessageProducer implements TopicPublisher +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRATopicPublisher.class); + + /** + * Create a new wrapper + * @param producer the producer + * @param session the session + */ + public QpidRATopicPublisher(final TopicPublisher producer, final QpidRASessionImpl session) + { + super(producer, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(producer) + ", " + session + ")"); + } + } + + /** + * Get the topic + * @return The topic + * @exception JMSException Thrown if an error occurs + */ + public Topic getTopic() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTopic()"); + } + + return ((TopicPublisher)_producer).getTopic(); + } + + /** + * Publish message + * @param message The message + * @param deliveryMode The delivery mode + * @param priority The priority + * @param timeToLive The time to live + * @exception JMSException Thrown if an error occurs + */ + public void publish(final Message message, final int deliveryMode, final int priority, final long timeToLive) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + + " message=" + + Util.asString(message) + + " deliveryMode=" + + deliveryMode + + " priority=" + + priority + + " ttl=" + + timeToLive); + } + + checkState(); + + ((TopicPublisher)_producer).publish(message, deliveryMode, priority, timeToLive); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Publish message + * @param message The message + * @exception JMSException Thrown if an error occurs + */ + public void publish(final Message message) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + " message=" + Util.asString(message)); + } + + checkState(); + + ((TopicPublisher)_producer).publish(message); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Publish message + * @param destination The destination + * @param message The message + * @param deliveryMode The delivery mode + * @param priority The priority + * @param timeToLive The time to live + * @exception JMSException Thrown if an error occurs + */ + public void publish(final Topic destination, + final Message message, + final int deliveryMode, + final int priority, + final long timeToLive) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + + " destination=" + + destination + + " message=" + + Util.asString(message) + + " deliveryMode=" + + deliveryMode + + " priority=" + + priority + + " ttl=" + + timeToLive); + } + + checkState(); + + ((TopicPublisher)_producer).publish(destination, message, deliveryMode, priority, timeToLive); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } + + /** + * Publish message + * @param destination The destination + * @param message The message + * @exception JMSException Thrown if an error occurs + */ + public void publish(final Topic destination, final Message message) throws JMSException + { + _session.lock(); + try + { + if (_log.isTraceEnabled()) + { + _log.trace("send " + this + " destination=" + destination + " message=" + Util.asString(message)); + } + + checkState(); + + ((TopicPublisher)_producer).publish(destination, message); + + if (_log.isTraceEnabled()) + { + _log.trace("sent " + this + " result=" + Util.asString(message)); + } + } + finally + { + _session.unlock(); + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicSubscriber.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicSubscriber.java new file mode 100644 index 0000000000..e423f468e0 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRATopicSubscriber.java @@ -0,0 +1,86 @@ +/* + * + * 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.qpid.ra; + +import javax.jms.JMSException; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for a topic subscriber + * + */ +public class QpidRATopicSubscriber extends QpidRAMessageConsumer implements TopicSubscriber +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRATopicSubscriber.class); + + /** + * Create a new wrapper + * @param consumer the topic subscriber + * @param session the session + */ + public QpidRATopicSubscriber(final TopicSubscriber consumer, final QpidRASessionImpl session) + { + super(consumer, session); + + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + Util.asString(consumer) + ", " + session + ")"); + } + } + + /** + * Get the no local value + * @return The value + * @exception JMSException Thrown if an error occurs + */ + public boolean getNoLocal() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getNoLocal()"); + } + + checkState(); + return ((TopicSubscriber)_consumer).getNoLocal(); + } + + /** + * Get the topic + * @return The topic + * @exception JMSException Thrown if an error occurs + */ + public Topic getTopic() throws JMSException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTopic()"); + } + + checkState(); + return ((TopicSubscriber)_consumer).getTopic(); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAXAResource.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAXAResource.java new file mode 100644 index 0000000000..22b39792b1 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidRAXAResource.java @@ -0,0 +1,245 @@ +/* + * + * 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.qpid.ra; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QpidRAXAResource. + * + */ +public class QpidRAXAResource implements XAResource +{ + /** The logger */ + private static final Logger _log = LoggerFactory.getLogger(QpidRAXAResource.class); + + /** The managed connection */ + private final QpidRAManagedConnection _managedConnection; + + /** The resource */ + private final XAResource _xaResource; + + /** + * Create a new QpidRAXAResource. + * @param managedConnection the managed connection + * @param xaResource the xa resource + */ + public QpidRAXAResource(final QpidRAManagedConnection managedConnection, final XAResource xaResource) + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + managedConnection + ", " + Util.asString(xaResource) + ")"); + } + + this._managedConnection = managedConnection; + this._xaResource = xaResource; + } + + /** + * Start + * @param xid A global transaction identifier + * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME + * @exception XAException An error has occurred + */ + public void start(final Xid xid, final int flags) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("start(" + xid + ", " + flags + ")"); + } + + _managedConnection.lock(); + try + { + _xaResource.start(xid, flags); + } + finally + { + _managedConnection.setInManagedTx(true); + _managedConnection.unlock(); + } + } + + /** + * End + * @param xid A global transaction identifier + * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND. + * @exception XAException An error has occurred + */ + public void end(final Xid xid, final int flags) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("end(" + xid + ", " + flags + ")"); + } + + _managedConnection.lock(); + try + { + _xaResource.end(xid, flags); + } + finally + { + _managedConnection.setInManagedTx(false); + _managedConnection.unlock(); + } + } + + /** + * Prepare + * @param xid A global transaction identifier + * @return XA_RDONLY or XA_OK + * @exception XAException An error has occurred + */ + public int prepare(final Xid xid) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("prepare(" + xid + ")"); + } + + return _xaResource.prepare(xid); + } + + /** + * Commit + * @param xid A global transaction identifier + * @param onePhase If true, the resource manager should use a one-phase commit protocol to commit the work done on behalf of xid. + * @exception XAException An error has occurred + */ + public void commit(final Xid xid, final boolean onePhase) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("commit(" + xid + ", " + onePhase + ")"); + } + + _xaResource.commit(xid, onePhase); + } + + /** + * Rollback + * @param xid A global transaction identifier + * @exception XAException An error has occurred + */ + public void rollback(final Xid xid) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("rollback(" + xid + ")"); + } + + _xaResource.rollback(xid); + } + + /** + * Forget + * @param xid A global transaction identifier + * @exception XAException An error has occurred + */ + public void forget(final Xid xid) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("forget(" + xid + ")"); + } + + _managedConnection.lock(); + try + { + _xaResource.forget(xid); + } + finally + { + _managedConnection.setInManagedTx(false); + _managedConnection.unlock(); + } + } + + /** + * IsSameRM + * @param xaRes An XAResource object whose resource manager instance is to be compared with the resource manager instance of the target object. + * @return True if its the same RM instance; otherwise false. + * @exception XAException An error has occurred + */ + public boolean isSameRM(final XAResource xaRes) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("isSameRM(" + xaRes + ")"); + } + + return _xaResource.isSameRM(xaRes); + } + + /** + * Recover + * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS + * @return Zero or more XIDs + * @exception XAException An error has occurred + */ + public Xid[] recover(final int flag) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("recover(" + flag + ")"); + } + + return _xaResource.recover(flag); + } + + /** + * Get the transaction timeout in seconds + * @return The transaction timeout + * @exception XAException An error has occurred + */ + public int getTransactionTimeout() throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionTimeout()"); + } + + return _xaResource.getTransactionTimeout(); + } + + /** + * Set the transaction timeout + * @param seconds The number of seconds + * @return True if the transaction timeout value is set successfully; otherwise false. + * @exception XAException An error has occurred + */ + public boolean setTransactionTimeout(final int seconds) throws XAException + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionTimeout(" + seconds + ")"); + } + + return _xaResource.setTransactionTimeout(seconds); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidResourceAdapter.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidResourceAdapter.java new file mode 100644 index 0000000000..d56f520db4 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/QpidResourceAdapter.java @@ -0,0 +1,820 @@ +/* + * + * 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.qpid.ra; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jms.Session; +import javax.jms.XASession; +import javax.resource.ResourceException; +import javax.resource.spi.ActivationSpec; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.ResourceAdapter; +import javax.resource.spi.ResourceAdapterInternalException; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.resource.spi.work.WorkManager; +import javax.transaction.TransactionManager; +import javax.transaction.xa.XAResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.XAConnectionImpl; +import org.apache.qpid.ra.inflow.QpidActivation; +import org.apache.qpid.ra.inflow.QpidActivationSpec; +import org.apache.qpid.url.URLSyntaxException; + +/** + * The resource adapter for Qpid + * + */ +public class QpidResourceAdapter implements ResourceAdapter, Serializable +{ + /** + * + */ + private static final long serialVersionUID = -2446231446818098726L; + + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(QpidResourceAdapter.class); + + /** + * The bootstrap context + */ + private BootstrapContext _ctx; + + /** + * The resource adapter properties + */ + private final QpidRAProperties _raProperties; + + /** + * Have the factory been configured + */ + private final AtomicBoolean _configured; + + /** + * The activations by activation spec + */ + private final Map<ActivationSpec, QpidActivation> _activations; + + private AMQConnectionFactory _defaultAMQConnectionFactory; + + private TransactionManager _tm; + + /** + * Constructor + */ + public QpidResourceAdapter() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + + _raProperties = new QpidRAProperties(); + _configured = new AtomicBoolean(false); + _activations = new ConcurrentHashMap<ActivationSpec, QpidActivation>(); + } + + public TransactionManager getTM() + { + return _tm; + } + + /** + * Endpoint activation + * + * @param endpointFactory The endpoint factory + * @param spec The activation spec + * @throws ResourceException Thrown if an error occurs + */ + public void endpointActivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec) throws ResourceException + { + if (!_configured.getAndSet(true)) + { + try + { + setup(); + } + catch (QpidRAException e) + { + throw new ResourceException("Unable to create activation", e); + } + } + if (_log.isTraceEnabled()) + { + _log.trace("endpointActivation(" + endpointFactory + ", " + spec + ")"); + } + + QpidActivation activation = new QpidActivation(this, endpointFactory, (QpidActivationSpec)spec); + _activations.put(spec, activation); + activation.start(); + } + + /** + * Endpoint deactivation + * + * @param endpointFactory The endpoint factory + * @param spec The activation spec + */ + public void endpointDeactivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec) + { + if (_log.isTraceEnabled()) + { + _log.trace("endpointDeactivation(" + endpointFactory + ", " + spec + ")"); + } + + QpidActivation activation = _activations.remove(spec); + if (activation != null) + { + activation.stop(); + } + } + + /** + * Get XA resources + * + * @param specs The activation specs + * @return The XA resources + * @throws ResourceException Thrown if an error occurs or unsupported + */ + public XAResource[] getXAResources(final ActivationSpec[] specs) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("getXAResources(" + specs + ")"); + } + + return null; + } + + /** + * Start + * + * @param ctx The bootstrap context + * @throws ResourceAdapterInternalException + * Thrown if an error occurs + */ + public void start(final BootstrapContext ctx) throws ResourceAdapterInternalException + { + if (_log.isTraceEnabled()) + { + _log.trace("start(" + ctx + ")"); + } + + locateTM(); + + this._ctx = ctx; + + _log.info("Qpid resource adapter started"); + } + + /** + * Stop + */ + public void stop() + { + if (_log.isTraceEnabled()) + { + _log.trace("stop()"); + } + + for (Map.Entry<ActivationSpec, QpidActivation> entry : _activations.entrySet()) + { + try + { + entry.getValue().stop(); + } + catch (Exception ignored) + { + _log.debug("Ignored", ignored); + } + } + + _activations.clear(); + + _log.info("Qpid resource adapter stopped"); + } + + /** + * Get the user name + * + * @return The value + */ + public String getDefaultUserName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUserName()"); + } + + return _raProperties.getUserName(); + } + + /** + * Set the user name + * + * @param userName The value + */ + public void setDefaultUserName(final String userName) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUserName(" + userName + ")"); + } + + _raProperties.setUserName(userName); + } + + /** + * Get the password + * + * @return The value + */ + public String getDefaultPassword() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPassword()"); + } + + return _raProperties.getPassword(); + } + + /** + * Set the password + * + * @param password The value + */ + public void setDefaultPassword(final String password) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPassword(****)"); + } + + _raProperties.setPassword(password); + } + + /** + * Get the client ID + * + * @return The value + */ + public String getClientId() + { + if (_log.isTraceEnabled()) + { + _log.trace("getClientID()"); + } + + return _raProperties.getClientId(); + } + + /** + * Set the client ID + * + * @param clientID The client id + */ + public void setClientId(final String clientID) + { + if (_log.isTraceEnabled()) + { + _log.trace("setClientID(" + clientID + ")"); + } + + _raProperties.setClientId(clientID); + } + + /** + * Get the host + * + * @return The value + */ + public String getHost() + { + if (_log.isTraceEnabled()) + { + _log.trace("getHost()"); + } + + return _raProperties.getHost(); + } + + /** + * Set the host + * + * @param host The host + */ + public void setHost(final String host) + { + if (_log.isTraceEnabled()) + { + _log.trace("setHost(" + host + ")"); + } + + _raProperties.setHost(host); + } + + /** + * Get the port + * + * @return The value + */ + public Integer getPort() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPort()"); + } + + return _raProperties.getPort(); + } + + /** + * Set the client ID + * + * @param port The port + */ + public void setPort(final Integer port) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPort(" + port + ")"); + } + + _raProperties.setPort(port); + } + + /** + * Get the connection url + * + * @return The value + */ + public String getPath() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPath()"); + } + + return _raProperties.getPath(); + } + + /** + * Set the client ID + * + * @param path The path + */ + public void setPath(final String path) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPath(" + path + ")"); + } + + _raProperties.setPath(path); + } + + /** + * Get the connection url + * + * @return The value + */ + public String getConnectionURL() + { + if (_log.isTraceEnabled()) + { + _log.trace("getConnectionURL()"); + } + + return _raProperties.getConnectionURL(); + } + + /** + * Set the client ID + * + * @param connectionURL The connection url + */ + public void setConnectionURL(final String connectionURL) + { + if (_log.isTraceEnabled()) + { + _log.trace("setConnectionURL(" + connectionURL + ")"); + } + + _raProperties.setConnectionURL(connectionURL); + } + + /** + * Get the transaction manager locator class + * + * @return The value + */ + public String getTransactionManagerLocatorClass() + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionManagerLocatorClass()"); + } + + return _raProperties.getTransactionManagerLocatorClass(); + } + + /** + * Set the transaction manager locator class + * + * @param locator The transaction manager locator class + */ + public void setTransactionManagerLocatorClass(final String locator) + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionManagerLocatorClass(" + locator + ")"); + } + + _raProperties.setTransactionManagerLocatorClass(locator); + } + + /** + * Get the transaction manager locator method + * + * @return The value + */ + public String getTransactionManagerLocatorMethod() + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionManagerLocatorMethod()"); + } + + return _raProperties.getTransactionManagerLocatorMethod(); + } + + /** + * Set the transaction manager locator method + * + * @param method The transaction manager locator method + */ + public void setTransactionManagerLocatorMethod(final String method) + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionManagerLocatorMethod(" + method + ")"); + } + + _raProperties.setTransactionManagerLocatorMethod(method); + } + + /** + * Get the use XA flag + * + * @return The value + */ + public Boolean getUseLocalTx() + { + if (_log.isTraceEnabled()) + { + _log.trace("getUseLocalTx()"); + } + + return _raProperties.getUseLocalTx(); + } + + /** + * Set the use XA flag + * + * @param localTx The value + */ + public void setUseLocalTx(final Boolean localTx) + { + if (_log.isTraceEnabled()) + { + _log.trace("setUseLocalTx(" + localTx + ")"); + } + + _raProperties.setUseLocalTx(localTx); + } + + public Integer getSetupAttempts() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupAttempts()"); + } + return _raProperties.getSetupAttempts(); + } + + public void setSetupAttempts(Integer setupAttempts) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupAttempts(" + setupAttempts + ")"); + } + _raProperties.setSetupAttempts(setupAttempts); + } + + public Long getSetupInterval() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupInterval()"); + } + return _raProperties.getSetupInterval(); + } + + public void setSetupInterval(Long interval) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupInterval(" + interval + ")"); + } + _raProperties.setSetupInterval(interval); + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param obj Object with which to compare + * @return True if this object is the same as the obj argument; false otherwise. + */ + public boolean equals(final Object obj) + { + if (obj == null) + { + return false; + } + + if (obj instanceof QpidResourceAdapter) + { + return _raProperties.equals(((QpidResourceAdapter)obj).getProperties()); + } + else + { + return false; + } + } + + /** + * Return the hash code for the object + * + * @return The hash code + */ + public int hashCode() + { + return _raProperties.hashCode(); + } + + /** + * Get the work manager + * + * @return The manager + */ + public WorkManager getWorkManager() + { + if (_log.isTraceEnabled()) + { + _log.trace("getWorkManager()"); + } + + if (_ctx == null) + { + return null; + } + + return _ctx.getWorkManager(); + } + + public XASession createXASession(final XAConnectionImpl connection) + throws Exception + { + final XASession result = connection.createXASession() ; + if (_log.isDebugEnabled()) + { + _log.debug("Using session " + Util.asString(result)); + } + return result ; + } + + public Session createSession(final AMQConnection connection, + final int ackMode, + final boolean useLocalTx, + final Integer prefetchLow, + final Integer prefetchHigh) throws Exception + { + Session result; + + if (prefetchLow == null) + { + result = connection.createSession(useLocalTx, ackMode) ; + } + else if (prefetchHigh == null) + { + result = connection.createSession(useLocalTx, ackMode, prefetchLow) ; + } + else + { + result = connection.createSession(useLocalTx, ackMode, prefetchHigh, prefetchLow) ; + } + + if (_log.isDebugEnabled()) + { + _log.debug("Using session " + Util.asString(result)); + } + + return result; + + } + + /** + * Get the resource adapter properties + * + * @return The properties + */ + protected QpidRAProperties getProperties() + { + if (_log.isTraceEnabled()) + { + _log.trace("getProperties()"); + } + + return _raProperties; + } + + /** + * Setup the factory + */ + protected void setup() throws QpidRAException + { + _defaultAMQConnectionFactory = createAMQConnectionFactory(_raProperties); + } + + + public AMQConnectionFactory getDefaultAMQConnectionFactory() throws ResourceException + { + if (!_configured.getAndSet(true)) + { + try + { + setup(); + } + catch (QpidRAException e) + { + throw new ResourceException("Unable to create activation", e); + } + } + return _defaultAMQConnectionFactory; + } + + public AMQConnectionFactory createAMQConnectionFactory(final ConnectionFactoryProperties overrideProperties) + throws QpidRAException + { + try + { + return createFactory(overrideProperties); + } + catch (final URLSyntaxException urlse) + { + throw new QpidRAException("Unexpected exception creating connection factory", urlse) ; + } + } + + public Map<String, Object> overrideConnectionParameters(final Map<String, Object> connectionParams, + final Map<String, Object> overrideConnectionParams) + { + Map<String, Object> map = new HashMap<String, Object>(); + + if(connectionParams != null) + { + map.putAll(connectionParams); + } + if(overrideConnectionParams != null) + { + for (Map.Entry<String, Object> stringObjectEntry : overrideConnectionParams.entrySet()) + { + map.put(stringObjectEntry.getKey(), stringObjectEntry.getValue()); + } + } + return map; + } + + private void locateTM() + { + if(_raProperties.getTransactionManagerLocatorClass() != null && _raProperties.getTransactionManagerLocatorMethod() != null) + { + + String locatorClasses[] = _raProperties.getTransactionManagerLocatorClass().split(";"); + String locatorMethods[] = _raProperties.getTransactionManagerLocatorMethod().split(";"); + + for (int i = 0 ; i < locatorClasses.length; i++) + { + _tm = Util.locateTM(locatorClasses[i], locatorMethods[i]); + if (_tm != null) + { + break; + } + } + + + } + + if (_tm == null) + { + _log.warn("It wasn't possible to lookup a Transaction Manager through the configured properties TransactionManagerLocatorClass and TransactionManagerLocatorMethod"); + _log.warn("Qpid Resource Adapter won't be able to set and verify transaction timeouts in certain cases."); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("TM located = " + _tm); + } + } + } + + + private AMQConnectionFactory createFactory(final ConnectionFactoryProperties overrideProperties) + throws URLSyntaxException, QpidRAException + { + final String overrideURL = overrideProperties.getConnectionURL() ; + final String url = overrideURL != null ? overrideURL : _raProperties.getConnectionURL() ; + + final String overrideClientID = overrideProperties.getClientId() ; + final String clientID = (overrideClientID != null ? overrideClientID : _raProperties.getClientId()) ; + + final String overrideDefaultPassword = overrideProperties.getPassword() ; + final String defaultPassword = (overrideDefaultPassword != null ? overrideDefaultPassword : _raProperties.getPassword()) ; + + final String overrideDefaultUsername = overrideProperties.getUserName() ; + final String defaultUsername = (overrideDefaultUsername != null ? overrideDefaultUsername : _raProperties.getUserName()) ; + + final String overrideHost = overrideProperties.getHost() ; + final String host = (overrideHost != null ? overrideHost : _raProperties.getHost()) ; + + final Integer overridePort = overrideProperties.getPort() ; + final Integer port = (overridePort != null ? overridePort : _raProperties.getPort()) ; + + final String overridePath = overrideProperties.getPath() ; + final String path = (overridePath != null ? overridePath : _raProperties.getPath()) ; + + final AMQConnectionFactory cf ; + + if (url != null) + { + cf = new AMQConnectionFactory(url) ; + + if (clientID != null) + { + cf.getConnectionURL().setClientName(clientID) ; + } + } + else + { + // create a URL to force the connection details + if ((host == null) || (port == null) || (path == null)) + { + throw new QpidRAException("Configuration requires host/port/path if connectionURL is not specified") ; + } + final String username = (defaultUsername != null ? defaultUsername : "") ; + final String password = (defaultPassword != null ? defaultPassword : "") ; + final String client = (clientID != null ? clientID : "") ; + + final String newurl = AMQConnectionURL.AMQ_PROTOCOL + "://" + username +":" + password + "@" + client + "/" + path + '?' + AMQConnectionURL.OPTIONS_BROKERLIST + "='tcp://" + host + ':' + port + '\'' ; + if (_log.isDebugEnabled()) + { + _log.debug("Initialising connectionURL to " + newurl) ; + } + + cf = new AMQConnectionFactory(newurl) ; + } + + return cf ; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/Util.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/Util.java new file mode 100644 index 0000000000..b927aaa0be --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/Util.java @@ -0,0 +1,184 @@ +/* + * + * 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.qpid.ra; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Method; + +import javax.naming.Context; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.transaction.TransactionManager; + +import org.apache.qpid.ra.admin.QpidQueue; +import org.apache.qpid.ra.admin.QpidTopic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Various utility functions + * + */ +public class Util +{ + + private static final Logger _log = LoggerFactory.getLogger(Util.class); + + /** + * Compare two strings. + * @param me First value + * @param you Second value + * @return True if object equals else false. + */ + public static boolean compare(final String me, final String you) + { + // If both null or intern equals + if (me == you) + { + return true; + } + + // if me null and you are not + if (me == null && you != null) + { + return false; + } + + // me will not be null, test for equality + return me.equals(you); + } + + /** + * Lookup an object in the default initial context + * @param context The context to use + * @param name the name to lookup + * @param clazz the expected type + * @return the object + * @throws Exception for any error + */ + public static <T> T lookup(final Context context, final String name, final Class<T> clazz) throws Exception + { + Object object = context.lookup(name); + + if (object instanceof Reference) + { + + Reference ref = (Reference) object; + String addressContent = null; + + if (ref.getClassName().equals(QpidQueue.class.getName())) + { + RefAddr addr = ref.get(QpidQueue.class.getName()); + addressContent = (String) addr.getContent(); + + if (addr != null) + { + return (T)new QpidQueue(addressContent); + } + } + + if (ref.getClassName().equals(QpidTopic.class.getName())) + { + RefAddr addr = ref.get(QpidTopic.class.getName()); + addressContent = (String) addr.getContent(); + + if (addr != null) + { + return (T)new QpidTopic(addressContent); + } + } + } + + return clazz.cast(object); + + } + + /** The Resource adapter can't depend on any provider's specific library. Because of that we use reflection to locate the + * transaction manager during startup. + * + * + * TODO: We should use a proper SPI instead of reflection + * We would need to define a proper SPI package for this. + **/ + public static TransactionManager locateTM(final String locatorClass, final String locatorMethod) + { + try + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class<?> aClass = loader.loadClass(locatorClass); + Object o = aClass.newInstance(); + Method m = aClass.getMethod(locatorMethod); + return (TransactionManager)m.invoke(o); + } + catch (Throwable e) + { + _log.debug(e.getMessage(), e); + return null; + } + } + + /** + * Serialize the object into a byte array. + * @param serializable The serializable object + * @return The generated byte array + * @throws IOException For errors during serialization. + */ + public static byte[] serialize(final Serializable serializable) + throws IOException + { + final ByteArrayOutputStream baos = new ByteArrayOutputStream() ; + final ObjectOutputStream oos = new ObjectOutputStream(baos) ; + oos.writeObject(serializable) ; + oos.close() ; + return baos.toByteArray() ; + } + + /** + * Deserialize the byte array into an object. + * @param data The serialized object as a byte array + * @return The serializable object. + * @throws IOException For errors during deserialization + * @throws ClassNotFoundException If the deserialized class cannot be found. + */ + public static Object deserialize(final byte[] data) + throws IOException, ClassNotFoundException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(data) ; + final ObjectInputStream ois = new ObjectInputStream(bais) ; + return ois.readObject() ; + } + + /** + * Return a string identification for the specified object. + * @param object The object value. + * @return The string identification. + */ + public static String asString(final Object object) + { + return (object == null ? "null" : object.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(object))) ; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/AdminObjectFactory.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/AdminObjectFactory.java new file mode 100644 index 0000000000..11f2903c72 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/AdminObjectFactory.java @@ -0,0 +1,74 @@ +/* + * + * 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.qpid.ra.admin; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AdminObjectFactory implements ObjectFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AdminObjectFactory.class); + + @Override + public Object getObjectInstance(Object object, Name name, Context context, Hashtable<?, ?> env) throws Exception + { + + Object instance = null; + + if (object instanceof Reference) + { + Reference ref = (Reference) object; + String bindingURLString; + + if (ref.getClassName().equals(QpidQueue.class.getName())) + { + RefAddr addr = ref.get(QpidQueue.class.getName()); + bindingURLString = (String) addr.getContent(); + + if (addr != null) + { + return new QpidQueue(bindingURLString); + } + + } + + if (ref.getClassName().equals(QpidTopic.class.getName())) + { + RefAddr addr = ref.get(QpidTopic.class.getName()); + bindingURLString = (String) addr.getContent(); + + if (addr != null) + { + return new QpidTopic(bindingURLString); + } + } + } + return instance; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidBindingURL.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidBindingURL.java new file mode 100644 index 0000000000..503f59eecc --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidBindingURL.java @@ -0,0 +1,52 @@ +/* + * + * 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.qpid.ra.admin; + +import java.net.URISyntaxException; + +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; + +public class QpidBindingURL extends AMQBindingURL { + + private String _url; + + public QpidBindingURL(String url) throws URISyntaxException { + super(url); + + if (!url.contains(BindingURL.OPTION_ROUTING_KEY) || getRoutingKey() == null) { + setOption(BindingURL.OPTION_ROUTING_KEY, null); + } + + this._url = url; + } + + @Override + public String getURL() { + return _url; + } + + @Override + public String toString() { + return _url; + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidConnectionFactoryProxy.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidConnectionFactoryProxy.java new file mode 100644 index 0000000000..41242fefae --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidConnectionFactoryProxy.java @@ -0,0 +1,156 @@ +/* + * + * 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.qpid.ra.admin; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; + +import javax.jms.ConnectionFactory; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.spi.ObjectFactory; + +import org.apache.qpid.client.AMQConnectionFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * + */ +public class QpidConnectionFactoryProxy implements Externalizable, Referenceable, ConnectionFactory, Serializable +{ + private static final Logger _log = LoggerFactory.getLogger(QpidDestinationProxy.class); + + private String _connectionURL; + + private ConnectionFactory _delegate; + + /** + * This constructor should not only be used be de-serialisation code. Create + * original object with the other constructor. + */ + public QpidConnectionFactoryProxy() + { + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + Reference ref = (Reference) in.readObject(); + + try + { + _delegate = (ConnectionFactory) dereference(ref); + + } catch (Exception e) + { + _log.error("Failed to dereference ConnectionFactory " + e.getMessage(), e); + throw new IOException("Failed to dereference ConnectionFactory: " + e.getMessage()); + } + } + + public void writeExternal(ObjectOutput out) throws IOException + { + if (_delegate == null) + { + _log.error("Null Destination "); + throw new IOException("Null ConnectionFactory!"); + } + + try + { + out.writeObject(((Referenceable) _delegate).getReference()); + } + catch (NamingException e) + { + _log.error("Failed to dereference ConnectionFactory " + e.getMessage(), e); + throw new IOException("Failed to dereference ConnectionFactory: " + e.getMessage()); + } + } + + @Override + public Reference getReference() throws NamingException + { + try + { + _delegate = new AMQConnectionFactory(getConnectionURL()); + /* + QpidResourceAdapter ra = new QpidResourceAdapter(); + QpidRAManagedConnectionFactory mcf = new QpidRAManagedConnectionFactory(); + mcf.setResourceAdapter(ra); + mcf.setConnectionURL(getConnectionURL()); + delegate = new QpidRAConnectionFactoryImpl(mcf, null); + */ + return ((Referenceable) _delegate).getReference(); + } + catch(Exception e) + { + throw new NamingException(e.getMessage()); + } + } + private Object dereference(Reference ref) throws Exception + { + ObjectFactory objFactory = (ObjectFactory) Class.forName( + ref.getFactoryClassName()).newInstance(); + return objFactory.getObjectInstance(ref, null, null, null); + } + + public void setConnectionURL(final String connectionURL) + { + this._connectionURL = connectionURL; + } + public String getConnectionURL() + { + return this._connectionURL; + } + + /** + * Create a connection + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public Connection createConnection() throws JMSException + { + return _delegate.createConnection(); + } + + /** + * Create a connection + * @param userName The user name + * @param password The password + * @return The connection + * @exception JMSException Thrown if the operation fails + */ + public Connection createConnection(final String userName, final String password) throws JMSException + { + return _delegate.createConnection(userName, password); + } + +} + diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidDestinationProxy.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidDestinationProxy.java new file mode 100644 index 0000000000..738ce4be0d --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidDestinationProxy.java @@ -0,0 +1,162 @@ +/* + * + * 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.qpid.ra.admin; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; + +import javax.jms.Destination; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.spi.ObjectFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The QpidDestinationProxy provides for allowing an administrator/developer to + * create and bind QPID destinations into a JNDI tree. AdminObjects are used as + * an generic integration point rather than relying on the EE server specific + * API's to create destinations (queues, topics). AdminObjects and associated + * properties are defined in the ra.xml file for a particular JCA adapter. + * Please see the ra.xml file for the QPID JCA resource adapter as well as the + * README.txt for the adapter for more details. + * + */ +public class QpidDestinationProxy implements Externalizable, Referenceable, Destination, Serializable +{ + private static final long serialVersionUID = -1137413782643796461L; + + private static final Logger _log = LoggerFactory.getLogger(QpidDestinationProxy.class); + + private static final String DEFAULT_QUEUE_TYPE = "QUEUE"; + + private static final String DEFAULT_TOPIC_TYPE = "TOPIC"; + + private String _destinationAddress; + + private String _destinationType; + + private Destination _delegate; + + /** + * This constructor should not only be used be de-serialisation code. Create + * original object with the other constructor. + */ + public QpidDestinationProxy() + { + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + Reference ref = (Reference) in.readObject(); + + try + { + _delegate = (Destination) dereference(ref); + + } catch (Exception e) + { + _log.error("Failed to dereference Destination " + e.getMessage(), e); + throw new IOException("Failed to dereference Destination: " + e.getMessage()); + } + } + + public void writeExternal(ObjectOutput out) throws IOException + { + if (_delegate == null) + { + _log.error("Null Destination "); + throw new IOException("Null destination!"); + } + + try + { + out.writeObject(((Referenceable) _delegate).getReference()); + } + catch (NamingException e) + { + _log.error("Failed to dereference Destination " + e.getMessage(), e); + throw new IOException("Failed to dereference Destination: " + e.getMessage()); + } + } + + @Override + public Reference getReference() throws NamingException + { + try + { + if(getDestinationType().equalsIgnoreCase(DEFAULT_QUEUE_TYPE)) + { + _delegate = new QpidQueue(getDestinationAddress()); + } + else if(getDestinationType().equalsIgnoreCase(DEFAULT_TOPIC_TYPE)) + { + _delegate = new QpidTopic(getDestinationAddress()); + } + else + { + throw new IllegalStateException("Unknown destination type " + getDestinationType()); + } + + return ((Referenceable) _delegate).getReference(); + + } + catch(Exception e) + { + _log.error(e.getMessage(),e); + throw new NamingException("Failed to create destination " + e.getMessage()); + } + + } + + private Object dereference(Reference ref) throws Exception + { + ObjectFactory objFactory = (ObjectFactory) Class.forName( + ref.getFactoryClassName()).newInstance(); + return objFactory.getObjectInstance(ref, null, null, null); + } + + public void setDestinationAddress(String destinationAddress) throws Exception + { + this._destinationAddress = destinationAddress; + } + + public String getDestinationAddress() + { + return this._destinationAddress; + } + + public void setDestinationType(String destinationType) + { + this._destinationType = destinationType; + } + + public String getDestinationType() + { + return this._destinationType; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidQueue.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidQueue.java new file mode 100644 index 0000000000..caef0c8ffd --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidQueue.java @@ -0,0 +1,51 @@ +/* + * + * 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.qpid.ra.admin; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.StringRefAddr; + +import org.apache.qpid.client.AMQQueue; + +public class QpidQueue extends AMQQueue +{ + private String _url; + + public QpidQueue(final String address) throws Exception + { + super(address); + this._url = address; + } + + @Override + public Reference getReference() throws NamingException + { + return new Reference(this.getClass().getName(), new StringRefAddr(this.getClass().getName(), toURL()), + AdminObjectFactory.class.getName(), null); + } + + @Override + public String toURL() + { + return _url; + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidTopic.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidTopic.java new file mode 100644 index 0000000000..3f181b93eb --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/admin/QpidTopic.java @@ -0,0 +1,52 @@ +/* + * + * 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.qpid.ra.admin; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.StringRefAddr; + +import org.apache.qpid.client.AMQTopic; + +public class QpidTopic extends AMQTopic +{ + private String _url; + + public QpidTopic(final String address) throws Exception + { + super(address); + this._url = address; + } + + @Override + public Reference getReference() throws NamingException + { + return new Reference(this.getClass().getName(), new StringRefAddr(this.getClass().getName(), toURL()), + AdminObjectFactory.class.getName(), null); + } + + @Override + public String toURL() + { + return _url; + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivation.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivation.java new file mode 100644 index 0000000000..98427d7f9d --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivation.java @@ -0,0 +1,593 @@ +/* + * + * 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.qpid.ra.inflow; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.resource.ResourceException; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.resource.spi.work.Work; +import javax.resource.spi.work.WorkManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.XAConnectionImpl; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.ra.QpidResourceAdapter; +import org.apache.qpid.ra.Util; + +/** + * The activation. + * + */ +public class QpidActivation implements ExceptionListener +{ + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(QpidActivation.class); + + /** + * The onMessage method + */ + public static final Method ONMESSAGE; + + /** + * The resource adapter + */ + private final QpidResourceAdapter _ra; + + /** + * The activation spec + */ + private final QpidActivationSpec _spec; + + /** + * The message endpoint factory + */ + private final MessageEndpointFactory _endpointFactory; + + /** + * Whether delivery is active + */ + private final AtomicBoolean _deliveryActive = new AtomicBoolean(false); + + /** + * The destination type + */ + private boolean _isTopic = false; + + /** + * Is the delivery transacted + */ + private boolean _isDeliveryTransacted; + + private Destination _destination; + + /** + * The connection + */ + private Connection _connection; + + private final List<QpidMessageHandler> _handlers = new ArrayList<QpidMessageHandler>(); + + private AMQConnectionFactory _factory; + + // Whether we are in the failure recovery loop + private AtomicBoolean _inFailure = new AtomicBoolean(false); + + static + { + try + { + ONMESSAGE = MessageListener.class.getMethod("onMessage", new Class[] { Message.class }); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + /** + * Constructor + * + * @param ra The resource adapter + * @param endpointFactory The endpoint factory + * @param spec The activation spec + * @throws ResourceException Thrown if an error occurs + */ + public QpidActivation(final QpidResourceAdapter ra, + final MessageEndpointFactory endpointFactory, + final QpidActivationSpec spec) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor(" + ra + ", " + endpointFactory + ", " + spec + ")"); + } + + this._ra = ra; + this._endpointFactory = endpointFactory; + this._spec = spec; + try + { + _isDeliveryTransacted = endpointFactory.isDeliveryTransacted(QpidActivation.ONMESSAGE); + } + catch (Exception e) + { + throw new ResourceException(e); + } + } + + /** + * Get the activation spec + * + * @return The value + */ + public QpidActivationSpec getActivationSpec() + { + if (_log.isTraceEnabled()) + { + _log.trace("getActivationSpec()"); + } + + return _spec; + } + + /** + * Get the message endpoint factory + * + * @return The value + */ + public MessageEndpointFactory getMessageEndpointFactory() + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageEndpointFactory()"); + } + + return _endpointFactory; + } + + /** + * Get whether delivery is transacted + * + * @return The value + */ + public boolean isDeliveryTransacted() + { + if (_log.isTraceEnabled()) + { + _log.trace("isDeliveryTransacted()"); + } + + return _isDeliveryTransacted; + } + + /** + * Get the work manager + * + * @return The value + */ + public WorkManager getWorkManager() + { + if (_log.isTraceEnabled()) + { + _log.trace("getWorkManager()"); + } + + return _ra.getWorkManager(); + } + + /** + * Is the destination a topic + * + * @return The value + */ + public boolean isTopic() + { + if (_log.isTraceEnabled()) + { + _log.trace("isTopic()"); + } + + return _isTopic; + } + + /** + * Start the activation + * + * @throws ResourceException Thrown if an error occurs + */ + public void start() throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("start()"); + } + _deliveryActive.set(true); + _ra.getWorkManager().scheduleWork(new SetupActivation()); + } + + /** + * Stop the activation + */ + public void stop() + { + if (_log.isTraceEnabled()) + { + _log.trace("stop()"); + } + + _deliveryActive.set(false); + teardown(); + } + + /** + * Setup the activation + * + * @throws Exception Thrown if an error occurs + */ + protected synchronized void setup() throws Exception + { + _log.debug("Setting up " + _spec); + setupCF(); + + setupDestination(); + final AMQConnection amqConnection ; + final boolean useLocalTx = _spec.isUseLocalTx() ; + final boolean isXA = _isDeliveryTransacted && !useLocalTx ; + + if (isXA) + { + amqConnection = (XAConnectionImpl)_factory.createXAConnection() ; + } + else + { + amqConnection = (AMQConnection)_factory.createConnection() ; + } + + amqConnection.setExceptionListener(this) ; + + for (int i = 0; i < _spec.getMaxSession(); i++) + { + Session session = null; + + try + { + if (isXA) + { + session = _ra.createXASession((XAConnectionImpl)amqConnection) ; + } + else + { + session = _ra.createSession((AMQConnection)amqConnection, + _spec.getAcknowledgeModeInt(), + useLocalTx, + _spec.getPrefetchLow(), + _spec.getPrefetchHigh()); + } + + _log.debug("Using session " + Util.asString(session)); + QpidMessageHandler handler = new QpidMessageHandler(this, _ra.getTM(), session); + handler.setup(); + _handlers.add(handler); + } + catch (Exception e) + { + try + { + amqConnection.close() ; + } + catch (Exception e2) + { + _log.trace("Ignored error closing connection", e2); + } + + throw e; + } + } + amqConnection.start() ; + this._connection = amqConnection ; + + _log.debug("Setup complete " + this); + } + + /** + * Teardown the activation + */ + protected synchronized void teardown() + { + _log.debug("Tearing down " + _spec); + + try + { + if (_connection != null) + { + _connection.stop(); + } + } + catch (Throwable t) + { + _log.debug("Error stopping connection " + Util.asString(_connection), t); + } + + for (QpidMessageHandler handler : _handlers) + { + handler.teardown(); + } + + try + { + if (_connection != null) + { + _connection.close(); + } + } + catch (Throwable t) + { + _log.debug("Error closing connection " + Util.asString(_connection), t); + } + if (_spec.isHasBeenUpdated()) + { + _factory = null; + } + _log.debug("Tearing down complete " + this); + } + + protected void setupCF() throws Exception + { + if (_spec.isHasBeenUpdated()) + { + _factory = _ra.createAMQConnectionFactory(_spec); + } + else + { + _factory = _ra.getDefaultAMQConnectionFactory(); + } + } + + public Destination getDestination() + { + return _destination; + } + + protected void setupDestination() throws Exception + { + + String destinationName = _spec.getDestination(); + String destinationTypeString = _spec.getDestinationType(); + + if (_spec.isUseJNDI()) + { + Context ctx = new InitialContext(); + _log.debug("Using context " + ctx.getEnvironment() + " for " + _spec); + if (_log.isTraceEnabled()) + { + _log.trace("setupDestination(" + ctx + ")"); + } + + if (destinationTypeString != null && !destinationTypeString.trim().equals("")) + { + _log.debug("Destination type defined as " + destinationTypeString); + + Class<? extends Destination> destinationType; + if (Topic.class.getName().equals(destinationTypeString)) + { + destinationType = Topic.class; + _isTopic = true; + } + else + { + destinationType = Queue.class; + } + + _log.debug("Retrieving destination " + destinationName + + " of type " + + destinationType.getName()); + _destination = Util.lookup(ctx, destinationName, destinationType); + //_destination = (Destination)ctx.lookup(destinationName); + + } + else + { + _log.debug("Destination type not defined"); + _log.debug("Retrieving destination " + destinationName + + " of type " + + Destination.class.getName()); + + _destination = Util.lookup(ctx, destinationName, AMQDestination.class); + _isTopic = !(_destination instanceof Queue) ; + } + } + else + { + _destination = (AMQDestination)AMQDestination.createDestination(_spec.getDestination()); + if (destinationTypeString != null && !destinationTypeString.trim().equals("")) + { + _log.debug("Destination type defined as " + destinationTypeString); + final boolean match ; + if (Topic.class.getName().equals(destinationTypeString)) + { + match = (_destination instanceof Topic) ; + _isTopic = true; + } + else + { + match = (_destination instanceof Queue) ; + } + if (!match) + { + throw new ClassCastException("Expected destination of type " + destinationTypeString + " but created destination " + _destination) ; + } + } + else + { + _isTopic = !(_destination instanceof Queue) ; + } + } + + _log.debug("Got destination " + _destination + " from " + destinationName); + } + + /** + * Get a string representation + * + * @return The value + */ + @Override + public String toString() + { + StringBuffer buffer = new StringBuffer(); + buffer.append(QpidActivation.class.getName()).append('('); + buffer.append("spec=").append(_spec.getClass().getName()); + buffer.append(" mepf=").append(_endpointFactory.getClass().getName()); + buffer.append(" active=").append(_deliveryActive.get()); + if (_spec.getDestination() != null) + { + buffer.append(" destination=").append(_spec.getDestination()); + } + buffer.append(" transacted=").append(_isDeliveryTransacted); + buffer.append(')'); + return buffer.toString(); + } + + public void onException(final JMSException jmse) + { + handleFailure(jmse) ; + } + + /** + * Handles any failure by trying to reconnect + * + * @param failure the reason for the failure + */ + public void handleFailure(Throwable failure) + { + if(doesNotExist(failure)) + { + _log.info("awaiting topic/queue creation " + getActivationSpec().getDestination()); + } + else + { + _log.warn("Failure in Qpid activation " + _spec, failure); + } + int reconnectCount = 0; + int setupAttempts = _spec.getSetupAttempts(); + long setupInterval = _spec.getSetupInterval(); + + // Only enter the failure loop once + if (_inFailure.getAndSet(true)) + return; + try + { + while (_deliveryActive.get() && (setupAttempts == -1 || reconnectCount < setupAttempts)) + { + teardown(); + + try + { + Thread.sleep(setupInterval); + } + catch (InterruptedException e) + { + _log.debug("Interrupted trying to reconnect " + _spec, e); + break; + } + + _log.info("Attempting to reconnect " + _spec); + try + { + setup(); + _log.info("Reconnected with Qpid"); + break; + } + catch (Throwable t) + { + if(doesNotExist(failure)) + { + _log.info("awaiting topic/queue creation " + getActivationSpec().getDestination()); + } + else + { + _log.error("Unable to reconnect " + _spec, t); + } + } + ++reconnectCount; + } + } + finally + { + // Leaving failure recovery loop + _inFailure.set(false); + } + } + + /** + * Check to see if the failure represents a missing endpoint + * @param failure The failure. + * @return true if it represents a missing endpoint, false otherwise + */ + private boolean doesNotExist(final Throwable failure) + { + return (failure instanceof AMQException) && (((AMQException)failure).getErrorCode() == AMQConstant.NOT_FOUND) ; + } + + /** + * Handles the setup + */ + private class SetupActivation implements Work + { + public void run() + { + try + { + setup(); + } + catch (Throwable t) + { + handleFailure(t); + } + } + + public void release() + { + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivationSpec.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivationSpec.java new file mode 100644 index 0000000000..5f4e2dcf6b --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidActivationSpec.java @@ -0,0 +1,604 @@ +/* + * + * 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.qpid.ra.inflow; + +import java.io.Serializable; + +import javax.jms.Session; +import javax.resource.ResourceException; +import javax.resource.spi.ActivationSpec; +import javax.resource.spi.InvalidPropertyException; +import javax.resource.spi.ResourceAdapter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.ra.ConnectionFactoryProperties; +import org.apache.qpid.ra.QpidResourceAdapter; + +/** + * The activation spec + * These properties are set on the MDB ActivactionProperties + * + */ +public class QpidActivationSpec extends ConnectionFactoryProperties implements ActivationSpec, Serializable +{ + private static final long serialVersionUID = 7379131936083146158L; + + private static final int DEFAULT_MAX_SESSION = 15; + + /** The logger */ + private static final transient Logger _log = LoggerFactory.getLogger(QpidActivationSpec.class); + + /** The resource adapter */ + private QpidResourceAdapter _ra; + + /** The destination */ + private String _destination; + + /** The destination type */ + private String _destinationType; + + /** The message selector */ + private String _messageSelector; + + /** The acknowledgement mode */ + private int _acknowledgeMode; + + /** The subscription durability */ + private boolean _subscriptionDurability; + + /** The subscription name */ + private String _subscriptionName; + + /** The maximum number of sessions */ + private Integer _maxSession; + + /** Transaction timeout */ + private Integer _transactionTimeout; + + /** prefetch low */ + private Integer _prefetchLow; + + /** prefetch high */ + private Integer _prefetchHigh; + + private boolean _useJNDI = true; + + // undefined by default, default is specified at the RA level in QpidRAProperties + private Integer _setupAttempts; + + // undefined by default, default is specified at the RA level in QpidRAProperties + private Long _setupInterval; + + /** + * Constructor + */ + public QpidActivationSpec() + { + if (_log.isTraceEnabled()) + { + _log.trace("constructor()"); + } + + _acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + _maxSession = DEFAULT_MAX_SESSION; + _transactionTimeout = 0; + } + + /** + * Get the resource adapter + * @return The resource adapter + */ + public ResourceAdapter getResourceAdapter() + { + if (_log.isTraceEnabled()) + { + _log.trace("getResourceAdapter()"); + } + + return _ra; + } + + /** + * @return the useJNDI + */ + public boolean isUseJNDI() + { + return _useJNDI; + } + + /** + * @param value the useJNDI to set + */ + public void setUseJNDI(final boolean value) + { + _useJNDI = value; + } + + /** + * Set the resource adapter + * @param ra The resource adapter + * @exception ResourceException Thrown if incorrect resource adapter + */ + public void setResourceAdapter(final ResourceAdapter ra) throws ResourceException + { + if (_log.isTraceEnabled()) + { + _log.trace("setResourceAdapter(" + ra + ")"); + } + + if (ra == null || !(ra instanceof QpidResourceAdapter)) + { + throw new ResourceException("Resource adapter is " + ra); + } + + this._ra = (QpidResourceAdapter)ra; + } + + /** + * Get the destination + * @return The value + */ + public String getDestination() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDestination()"); + } + + return _destination; + } + + /** + * Set the destination + * @param value The value + */ + public void setDestination(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDestination(" + value + ")"); + } + + _destination = value; + } + + /** + * Get the destination type + * @return The value + */ + public String getDestinationType() + { + if (_log.isTraceEnabled()) + { + _log.trace("getDestinationType()"); + } + + return _destinationType; + } + + /** + * Set the destination type + * @param value The value + */ + public void setDestinationType(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setDestinationType(" + value + ")"); + } + + _destinationType = value; + } + + /** + * Get the message selector + * @return The value + */ + public String getMessageSelector() + { + if (_log.isTraceEnabled()) + { + _log.trace("getMessageSelector()"); + } + + return _messageSelector; + } + + /** + * Set the message selector + * @param value The value + */ + public void setMessageSelector(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setMessageSelector(" + value + ")"); + } + + _messageSelector = value; + } + + /** + * Get the acknowledge mode + * @return The value + */ + public String getAcknowledgeMode() + { + if (_log.isTraceEnabled()) + { + _log.trace("getAcknowledgeMode()"); + } + + if (Session.DUPS_OK_ACKNOWLEDGE == _acknowledgeMode) + { + return "Dups-ok-acknowledge"; + } + else + { + return "Auto-acknowledge"; + } + } + + /** + * Set the acknowledge mode + * @param value The value + */ + public void setAcknowledgeMode(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setAcknowledgeMode(" + value + ")"); + } + + if ("DUPS_OK_ACKNOWLEDGE".equalsIgnoreCase(value) || "Dups-ok-acknowledge".equalsIgnoreCase(value)) + { + _acknowledgeMode = Session.DUPS_OK_ACKNOWLEDGE; + } + else if ("AUTO_ACKNOWLEDGE".equalsIgnoreCase(value) || "Auto-acknowledge".equalsIgnoreCase(value)) + { + _acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + } + else + { + throw new IllegalArgumentException("Unsupported acknowledgement mode " + value); + } + } + + /** + * @return the acknowledgement mode + */ + public int getAcknowledgeModeInt() + { + if (_log.isTraceEnabled()) + { + _log.trace("getAcknowledgeMode()"); + } + + return _acknowledgeMode; + } + + /** + * Get the subscription durability + * @return The value + */ + public String getSubscriptionDurability() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSubscriptionDurability()"); + } + + if (_subscriptionDurability) + { + return "Durable"; + } + else + { + return "NonDurable"; + } + } + + /** + * Set the subscription durability + * @param value The value + */ + public void setSubscriptionDurability(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSubscriptionDurability(" + value + ")"); + } + + _subscriptionDurability = "Durable".equals(value); + } + + /** + * Get the status of subscription durability + * @return The value + */ + public boolean isSubscriptionDurable() + { + if (_log.isTraceEnabled()) + { + _log.trace("isSubscriptionDurable()"); + } + + return _subscriptionDurability; + } + + /** + * Get the subscription name + * @return The value + */ + public String getSubscriptionName() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSubscriptionName()"); + } + + return _subscriptionName; + } + + /** + * Set the subscription name + * @param value The value + */ + public void setSubscriptionName(final String value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSubscriptionName(" + value + ")"); + } + + _subscriptionName = value; + } + + /** + * Get the number of max session + * @return The value + */ + public Integer getMaxSession() + { + if (_log.isTraceEnabled()) + { + _log.trace("getMaxSession()"); + } + + if (_maxSession == null) + { + return DEFAULT_MAX_SESSION; + } + + return _maxSession; + } + + /** + * Set the number of max session + * @param value The value + */ + public void setMaxSession(final Integer value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setMaxSession(" + value + ")"); + } + + _maxSession = value; + } + + /** + * Get the transaction timeout + * @return The value + */ + public Integer getTransactionTimeout() + { + if (_log.isTraceEnabled()) + { + _log.trace("getTransactionTimeout()"); + } + + return _transactionTimeout; + } + + /** + * Set the transaction timeout + * @param value The value + */ + public void setTransactionTimeout(final Integer value) + { + if (_log.isTraceEnabled()) + { + _log.trace("setTransactionTimeout(" + value + ")"); + } + + _transactionTimeout = value; + } + + /** + * Get the prefetch low + * @return The value + */ + public Integer getPrefetchLow() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPrefetchLow()"); + } + + return _prefetchLow; + } + + /** + * Set the prefetch low + * @param value The value + */ + public void setPrefetchLow(final Integer prefetchLow) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPrefetchLow(" + prefetchLow + ")"); + } + + this._prefetchLow = prefetchLow; + } + + /** + * Get the prefetch high + * @return The value + */ + public Integer getPrefetchHigh() + { + if (_log.isTraceEnabled()) + { + _log.trace("getPrefetchHigh()"); + } + + return _prefetchHigh; + } + + /** + * Set the prefetch high + * @param value The value + */ + public void setPrefetchHigh(final Integer prefetchHigh) + { + if (_log.isTraceEnabled()) + { + _log.trace("setPrefetchHigh(" + prefetchHigh + ")"); + } + + this._prefetchHigh = prefetchHigh; + } + + public int getSetupAttempts() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupAttempts()"); + } + + if (_setupAttempts == null) + { + return _ra.getSetupAttempts(); + } + else + { + return _setupAttempts; + } + } + + public void setSetupAttempts(int setupAttempts) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupAttempts(" + setupAttempts + ")"); + } + + this._setupAttempts = setupAttempts; + } + + public long getSetupInterval() + { + if (_log.isTraceEnabled()) + { + _log.trace("getSetupInterval()"); + } + + if (_setupInterval == null) + { + return _ra.getSetupInterval(); + } + else + { + return _setupInterval; + } + } + + public void setSetupInterval(long setupInterval) + { + if (_log.isTraceEnabled()) + { + _log.trace("setSetupInterval(" + setupInterval + ")"); + } + + this._setupInterval = setupInterval; + } + + /** + * Validate + * @exception InvalidPropertyException Thrown if a validation exception occurs + */ + public void validate() throws InvalidPropertyException + { + if (_log.isTraceEnabled()) + { + _log.trace("validate()"); + } + + if (_destination == null || _destination.trim().equals("")) + { + throw new InvalidPropertyException("Destination is mandatory"); + } + } + + /** + * Get a string representation + * @return The value + */ + @Override + public String toString() + { + StringBuffer buffer = new StringBuffer(); + buffer.append(QpidActivationSpec.class.getName()).append('('); + buffer.append("ra=").append(_ra); + buffer.append(" destination=").append(_destination); + buffer.append(" destinationType=").append(_destinationType); + if (_messageSelector != null) + { + buffer.append(" selector=").append(_messageSelector); + } + buffer.append(" ack=").append(getAcknowledgeMode()); + buffer.append(" durable=").append(_subscriptionDurability); + buffer.append(" clientID=").append(getClientId()); + if (_subscriptionName != null) + { + buffer.append(" subscription=").append(_subscriptionName); + } + buffer.append(" user=").append(getUserName()); + if (getPassword() != null) + { + buffer.append(" password=").append("****"); + } + buffer.append(" maxSession=").append(_maxSession); + if (_prefetchLow != null) + { + buffer.append(" prefetchLow=").append(_prefetchLow); + } + if (_prefetchHigh != null) + { + buffer.append(" prefetchHigh=").append(_prefetchHigh); + } + buffer.append(')'); + return buffer.toString(); + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidMessageHandler.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidMessageHandler.java new file mode 100644 index 0000000000..473efab31f --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/inflow/QpidMessageHandler.java @@ -0,0 +1,245 @@ +/* + * + * 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.qpid.ra.inflow; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.XASession; +import javax.resource.ResourceException; +import javax.resource.spi.endpoint.MessageEndpoint; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.transaction.Status; +import javax.transaction.TransactionManager; +import javax.transaction.xa.XAResource; + +import org.apache.qpid.ra.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The message handler + * + */ +public class QpidMessageHandler implements MessageListener +{ + /** + * The logger + */ + private static final Logger _log = LoggerFactory.getLogger(QpidMessageHandler.class); + + /** + * The session + */ + private final Session _session; + + private MessageConsumer _consumer; + + /** + * The endpoint + */ + private MessageEndpoint _endpoint; + + private final QpidActivation _activation; + + private boolean _useLocalTx; + + private boolean _transacted; + + private final TransactionManager _tm; + + public QpidMessageHandler(final QpidActivation activation, + final TransactionManager tm, + final Session session) + { + this._activation = activation; + this._session = session; + this._tm = tm; + } + + public void setup() throws Exception + { + if (_log.isTraceEnabled()) + { + _log.trace("setup()"); + } + + QpidActivationSpec spec = _activation.getActivationSpec(); + String selector = spec.getMessageSelector(); + + // Create the message consumer + if (_activation.isTopic()) + { + final Topic topic = (Topic) _activation.getDestination(); + final String subscriptionName = spec.getSubscriptionName(); + if (spec.isSubscriptionDurable()) + _consumer = _session.createDurableSubscriber(topic, subscriptionName, selector, false); + else + _consumer = _session.createConsumer(topic, selector) ; + } + else + { + final Queue queue = (Queue) _activation.getDestination(); + _consumer = _session.createConsumer(queue, selector); + } + + // Create the endpoint, if we are transacted pass the session so it is enlisted, unless using Local TX + MessageEndpointFactory endpointFactory = _activation.getMessageEndpointFactory(); + _useLocalTx = _activation.getActivationSpec().isUseLocalTx(); + _transacted = _activation.isDeliveryTransacted() || _useLocalTx ; + if (_activation.isDeliveryTransacted() && !_activation.getActivationSpec().isUseLocalTx()) + { + final XAResource xaResource = ((XASession)_session).getXAResource() ; + _endpoint = endpointFactory.createEndpoint(xaResource); + } + else + { + _endpoint = endpointFactory.createEndpoint(null); + } + _consumer.setMessageListener(this); + } + + /** + * Stop the handler + */ + public void teardown() + { + if (_log.isTraceEnabled()) + { + _log.trace("teardown()"); + } + + try + { + if (_endpoint != null) + { + _endpoint.release(); + _endpoint = null; + } + } + catch (Throwable t) + { + _log.debug("Error releasing endpoint " + _endpoint, t); + } + } + + public void onMessage(final Message message) + { + if (_log.isTraceEnabled()) + { + _log.trace("onMessage(" + Util.asString(message) + ")"); + } + + boolean beforeDelivery = false; + + try + { + if (_activation.getActivationSpec().getTransactionTimeout() > 0 && _tm != null) + { + _tm.setTransactionTimeout(_activation.getActivationSpec().getTransactionTimeout()); + } + + _endpoint.beforeDelivery(QpidActivation.ONMESSAGE); + beforeDelivery = true; + + if(_transacted) + { + message.acknowledge(); + } + + ((MessageListener)_endpoint).onMessage(message); + + if (_transacted && (_tm.getTransaction() != null)) + { + final int status = _tm.getStatus() ; + final boolean rollback = status == Status.STATUS_MARKED_ROLLBACK + || status == Status.STATUS_ROLLING_BACK + || status == Status.STATUS_ROLLEDBACK; + if (rollback) + { + _session.recover() ; + } + } + else + { + message.acknowledge(); + } + + try + { + _endpoint.afterDelivery(); + } + catch (ResourceException e) + { + _log.warn("Unable to call after delivery", e); + return; + } + if (_useLocalTx) + { + _session.commit(); + } + } + catch (Throwable e) + { + _log.error("Failed to deliver message", e); + // we need to call before/afterDelivery as a pair + if (beforeDelivery) + { + try + { + _endpoint.afterDelivery(); + } + catch (ResourceException e1) + { + _log.warn("Unable to call after delivery", e); + } + } + if (_useLocalTx || !_activation.isDeliveryTransacted()) + { + try + { + _session.rollback(); + } + catch (JMSException e1) + { + _log.warn("Unable to roll local transaction back", e1); + } + } + else + { + try + { + _session.recover() ; + } + catch (JMSException e1) + { + _log.warn("Unable to recover XA transaction", e1); + } + } + } + + } + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/GeronimoTransactionManagerLocator.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/GeronimoTransactionManagerLocator.java new file mode 100644 index 0000000000..3a47824631 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/GeronimoTransactionManagerLocator.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.qpid.ra.tm; + +import java.util.Set; + +import javax.transaction.TransactionManager; + +import org.apache.geronimo.gbean.AbstractName; +import org.apache.geronimo.gbean.AbstractNameQuery; +import org.apache.geronimo.kernel.Kernel; +import org.apache.geronimo.kernel.KernelRegistry; + +public class GeronimoTransactionManagerLocator +{ + + public GeronimoTransactionManagerLocator() + { + } + + public TransactionManager getTransactionManager() + { + try + { + Kernel kernel = KernelRegistry.getSingleKernel(); + AbstractNameQuery query = new AbstractNameQuery(TransactionManager.class.getName ()); + Set<AbstractName> names = kernel.listGBeans(query); + + if (names.size() != 1) + { + throw new IllegalStateException("Expected one transaction manager, not " + names.size()); + } + + AbstractName name = names.iterator().next(); + TransactionManager transMg = (TransactionManager) kernel.getGBean(name); + return (TransactionManager)transMg; + + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBoss7TransactionManagerLocator.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBoss7TransactionManagerLocator.java new file mode 100644 index 0000000000..266c56bd63 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBoss7TransactionManagerLocator.java @@ -0,0 +1,33 @@ +package org.apache.qpid.ra.tm; + +import javax.naming.InitialContext; +import javax.transaction.TransactionManager; + +public class JBoss7TransactionManagerLocator +{ + private static final String TM_JNDI_NAME = "java:jboss/TransactionManager"; + + public TransactionManager getTm() throws Exception + { + InitialContext ctx = null; + + try + { + ctx = new InitialContext(); + return (TransactionManager)ctx.lookup(TM_JNDI_NAME); + } + finally + { + try + { + if(ctx != null) + { + ctx.close(); + } + } + catch(Exception ignore) + { + } + } + } +} diff --git a/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBossTransactionManagerLocator.java b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBossTransactionManagerLocator.java new file mode 100644 index 0000000000..5a5b585984 --- /dev/null +++ b/qpid/java/jca/src/main/java/org/apache/qpid/ra/tm/JBossTransactionManagerLocator.java @@ -0,0 +1,70 @@ +/* + * + * 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.qpid.ra.tm; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.transaction.TransactionManager; + +/** + */ +public class JBossTransactionManagerLocator +{ + private final String LOCATOR = "org.jboss.tm.TransactionManagerLocator" ; + + public TransactionManager getTm() + throws SecurityException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException + { + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader() ; + final Class<?> locatorClass ; + try + { + locatorClass = classLoader.loadClass(LOCATOR) ; + } + catch (final ClassNotFoundException cnfe) + { + return null ; + } + + Method instanceMethod = null ; + try + { + instanceMethod = locatorClass.getMethod("getInstance") ; + } + catch (final NoSuchMethodException nsme) {} // ignore + + final Object instance ; + final String locatorMethodName ; + if (instanceMethod != null) + { + instance = instanceMethod.invoke(null) ; + locatorMethodName = "locate" ; + } + else + { + instance = null ; + locatorMethodName = "locateTransactionManager" ; + } + final Method locatorMethod = locatorClass.getMethod(locatorMethodName) ; + return (TransactionManager) locatorMethod.invoke(instance) ; + } +} diff --git a/qpid/java/systests/etc/virtualhosts-systests-aclv2-settings.xml b/qpid/java/jca/src/main/resources/META-INF/jboss-ra.xml index db1ad33a39..f459b1efc1 100644 --- a/qpid/java/systests/etc/virtualhosts-systests-aclv2-settings.xml +++ b/qpid/java/jca/src/main/resources/META-INF/jboss-ra.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> +<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed to the Apache Software Foundation (ASF) under one @@ -19,30 +19,15 @@ - under the License. - --> -<virtualhosts> - <virtualhost> - <name>test</name> - <test> - <queues> - <exchange>amq.direct</exchange> - <!-- 4Mb --> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 2Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 10 mins --> - <maximumMessageAge>600000</maximumMessageAge> - </queues> - - <security> - <aclv2>${QPID_HOME}/etc/test-default.txt</aclv2> - </security> - </test> - </virtualhost> - - <virtualhost> - <name>test2</name> - <test2 /> - </virtualhost> -</virtualhosts> - - +<jboss-ra> + <ra-config-property> + <ra-config-property-name>TransactionManagerLocatorClass</ra-config-property-name> + <ra-config-property-type>java.lang.String</ra-config-property-type> + <ra-config-property-value>org.apache.qpid.ra.tm.JBossTransactionManagerLocator</ra-config-property-value> + </ra-config-property> + <ra-config-property> + <ra-config-property-name>TransactionManagerLocatorMethod</ra-config-property-name> + <ra-config-property-type>java.lang.String</ra-config-property-type> + <ra-config-property-value>getTm</ra-config-property-value> + </ra-config-property> +</jboss-ra> diff --git a/qpid/java/jca/src/main/resources/META-INF/ra.xml b/qpid/java/jca/src/main/resources/META-INF/ra.xml new file mode 100755 index 0000000000..90dc7b3b8e --- /dev/null +++ b/qpid/java/jca/src/main/resources/META-INF/ra.xml @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + - + - 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. + - + --> + +<connector xmlns="http://java.sun.com/xml/ns/j2ee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee + http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd" + version="1.5"> + + <description>QPID Resource Adapter</description> + <display-name>QPID Resource Adapter</display-name> + + <vendor-name>Apache Software Foundation</vendor-name> + <eis-type>JMS 1.1 Server</eis-type> + <resourceadapter-version>1.0</resourceadapter-version> + + <license> + <description> + 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. + </description> + <license-required>true</license-required> + </license> + + <resourceadapter> + <resourceadapter-class>org.apache.qpid.ra.QpidResourceAdapter</resourceadapter-class> + <config-property> + <description>Client ID for the connection</description> + <config-property-name>ClientId</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>client_id</config-property-value> + </config-property> + + <config-property> + <description>Number of setup attempts before failing</description> + <config-property-name>SetupAttempts</config-property-name> + <config-property-type>java.lang.Integer</config-property-type> + <config-property-value>5</config-property-value> + </config-property> + + <config-property> + <description>Interval between setup attempts</description> + <config-property-name>SetupInterval</config-property-name> + <config-property-type>java.lang.Long</config-property-type> + <config-property-value>5000</config-property-value> + </config-property> + + <config-property> + <description>Use local transactions rather than XA</description> + <config-property-name>UseLocalTx</config-property-name> + <config-property-type>java.lang.Boolean</config-property-type> + <config-property-value>false</config-property-value> + </config-property> + + <config-property> + <description>Broker host</description> + <config-property-name>Host</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>localhost</config-property-value> + </config-property> + + <config-property> + <description>Broker port</description> + <config-property-name>Port</config-property-name> + <config-property-type>java.lang.Integer</config-property-type> + <config-property-value>5672</config-property-value> + </config-property> + + <config-property> + <description>Virtual Path for Connection Factory</description> + <config-property-name>Path</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>test</config-property-value> + </config-property> + + <config-property> + <description>connection URL</description> + <config-property-name>ConnectionURL</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'</config-property-value> + </config-property> + + <outbound-resourceadapter> + <connection-definition> + <managedconnectionfactory-class>org.apache.qpid.ra.QpidRAManagedConnectionFactory</managedconnectionfactory-class> + + <config-property> + <description>Default session type</description> + <config-property-name>sessionDefaultType</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>javax.jms.Queue</config-property-value> + </config-property> + + <config-property> + <description>Specify lock timeout in seconds</description> + <config-property-name>useTryLock</config-property-name> + <config-property-type>java.lang.Integer</config-property-type> + <config-property-value>0</config-property-value> + </config-property> + + <config-property> + <description>Use local transactions rather than XA</description> + <config-property-name>UseLocalTx</config-property-name> + <config-property-type>java.lang.Boolean</config-property-type> + <config-property-value>false</config-property-value> + </config-property> + + <config-property> + <description>Client ID for the connection</description> + <config-property-name>ClientID</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>client_id</config-property-value> + </config-property> + + <config-property> + <description>Connection URL</description> + <config-property-name>ConnectionURL</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value></config-property-value> + </config-property> + + <config-property> + <description>Broker host</description> + <config-property-name>Host</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>localhost</config-property-value> + </config-property> + + <config-property> + <description>Broker port</description> + <config-property-name>Port</config-property-name> + <config-property-type>java.lang.Integer</config-property-type> + <config-property-value>5672</config-property-value> + </config-property> + + <config-property> + <description>Virtual Path for Connection Factory</description> + <config-property-name>Path</config-property-name> + <config-property-type>java.lang.String</config-property-type> + <config-property-value>test</config-property-value> + </config-property> + + <connectionfactory-interface>org.apache.qpid.ra.QpidRAConnectionFactory</connectionfactory-interface> + <connectionfactory-impl-class>org.apache.qpid.ra.QpidRAConnectionFactoryImpl</connectionfactory-impl-class> + <connection-interface>javax.jms.Session</connection-interface> + <connection-impl-class>org.apache.qpid.ra.QpidRASessionImpl</connection-impl-class> + </connection-definition> + <transaction-support>XATransaction</transaction-support> + <authentication-mechanism> + <authentication-mechanism-type>BasicPassword</authentication-mechanism-type> + <credential-interface>javax.resource.spi.security.PasswordCredential</credential-interface> + </authentication-mechanism> + <reauthentication-support>false</reauthentication-support> + </outbound-resourceadapter> + <inbound-resourceadapter> + <messageadapter> + <messagelistener> + <messagelistener-type>javax.jms.MessageListener</messagelistener-type> + <activationspec> + <activationspec-class>org.apache.qpid.ra.inflow.QpidActivationSpec</activationspec-class> + <required-config-property> + <config-property-name>destination</config-property-name> + </required-config-property> + </activationspec> + </messagelistener> + </messageadapter> + </inbound-resourceadapter> + + <adminobject> + <adminobject-interface>javax.jms.Destination</adminobject-interface> + <adminobject-class> org.apache.qpid.ra.admin.QpidDestinationProxy</adminobject-class> + <config-property> + <config-property-name>destinationAddress </config-property-name> + <config-property-type>java.lang.String </config-property-type> + </config-property> + <config-property> + <config-property-name>destinationType</config-property-name> + <config-property-type>java.lang.String </config-property-type> + </config-property> + </adminobject> + <adminobject> + <adminobject-interface>javax.jms.ConnectionFactory</adminobject-interface> + <adminobject-class> org.apache.qpid.ra.admin.QpidConnectionFactoryProxy</adminobject-class> + <config-property> + <config-property-name>connectionURL</config-property-name> + <config-property-type>java.lang.String </config-property-type> + </config-property> + </adminobject> + </resourceadapter> +</connector> diff --git a/qpid/java/lib/geronimo-ejb_3.0_spec-1.0.1.jar b/qpid/java/lib/geronimo-ejb_3.0_spec-1.0.1.jar Binary files differnew file mode 100644 index 0000000000..29087f2f46 --- /dev/null +++ b/qpid/java/lib/geronimo-ejb_3.0_spec-1.0.1.jar diff --git a/qpid/java/lib/geronimo-j2ee-connector_1.5_spec-2.0.0.jar b/qpid/java/lib/geronimo-j2ee-connector_1.5_spec-2.0.0.jar Binary files differnew file mode 100644 index 0000000000..70e7ed4a93 --- /dev/null +++ b/qpid/java/lib/geronimo-j2ee-connector_1.5_spec-2.0.0.jar diff --git a/qpid/java/lib/geronimo-jta_1.1_spec-1.1.1.jar b/qpid/java/lib/geronimo-jta_1.1_spec-1.1.1.jar Binary files differnew file mode 100644 index 0000000000..ee9963dfc8 --- /dev/null +++ b/qpid/java/lib/geronimo-jta_1.1_spec-1.1.1.jar diff --git a/qpid/java/lib/geronimo-kernel-2.2.1.jar b/qpid/java/lib/geronimo-kernel-2.2.1.jar Binary files differnew file mode 100644 index 0000000000..34c46c3183 --- /dev/null +++ b/qpid/java/lib/geronimo-kernel-2.2.1.jar diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java index b5c80a4fed..b74342df1f 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java @@ -23,6 +23,7 @@ package org.apache.qpid.management.common.mbeans; import java.io.IOException; import java.util.List; +import java.util.Map; import javax.management.JMException; import javax.management.MBeanException; @@ -118,6 +119,24 @@ public interface ManagedBroker throws IOException, JMException, MBeanException; /** + * Create a new Queue in the VirtualHost + * + * @since Qpid JMX API 2.4 + * @param queueName name of the new queue + * @param durable true if the queue should be durable + * @param owner owner + * @param arguments declaration arguments for use when creating the queue, may be null. + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="createNewQueue", description="Create a new Queue in the VirtualHost", impact= MBeanOperationInfo.ACTION) + void createNewQueue(@MBeanOperationParameter(name="queue name", description="Name of the new queue")String queueName, + @MBeanOperationParameter(name="owner", description="Owner name")String owner, + @MBeanOperationParameter(name="durable", description="true if the queue should be durable")boolean durable, + @MBeanOperationParameter(name="arguments", description="Map of arguments")Map<String,Object> arguments) + throws IOException, JMException; + + /** * Unregisters the Queue bindings, removes the subscriptions and unregisters * from the managed objects. * diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java index d16db65d5d..c2900a3533 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java @@ -80,33 +80,12 @@ public interface ManagedConnection Date getLastIoTime(); /** - * Tells the total number of bytes written till now. - * @return number of bytes written. - * - @MBeanAttribute(name="WrittenBytes", description="The total number of bytes written till now") - Long getWrittenBytes(); - */ - /** - * Tells the total number of bytes read till now. - * @return number of bytes read. - * - @MBeanAttribute(name="ReadBytes", description="The total number of bytes read till now") - Long getReadBytes(); - */ - - /** * Threshold high value for no of channels. This is useful in setting notifications or * taking required action is there are more channels being created. * @return threshold limit for no of channels */ - Long getMaximumNumberOfChannels(); - - /** - * Sets the threshold high value for number of channels for a connection - * @param value - */ @MBeanAttribute(name="MaximumNumberOfChannels", description="The threshold high value for number of channels for this connection") - void setMaximumNumberOfChannels(Long value); + Long getMaximumNumberOfChannels(); //********** Operations *****************// diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java index be31d8ef88..c23a0f5076 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java @@ -50,7 +50,8 @@ public interface ManagedQueue String MSG_SIZE = "Size(bytes)"; String MSG_REDELIVERED = "Redelivered"; String MSG_QUEUE_POS = "Queue Position"; - List<String> VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC = Collections.unmodifiableList(Arrays.asList(MSG_AMQ_ID, MSG_HEADER, MSG_SIZE, MSG_REDELIVERED, MSG_QUEUE_POS)); + String MSG_DELIVERY_COUNT = "Delivery Count"; + List<String> VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC = Collections.unmodifiableList(Arrays.asList(MSG_AMQ_ID, MSG_HEADER, MSG_SIZE, MSG_REDELIVERED, MSG_QUEUE_POS, MSG_DELIVERY_COUNT)); List<String> VIEW_MSGS_TABULAR_UNIQUE_INDEX = Collections.unmodifiableList(Arrays.asList(MSG_QUEUE_POS)); //CompositeType key/description information for message content @@ -67,6 +68,7 @@ public interface ManagedQueue static final String ATTR_MAX_MSG_COUNT = "MaximumMessageCount"; static final String ATTR_MAX_QUEUE_DEPTH = "MaximumQueueDepth"; static final String ATTR_MAX_MSG_SIZE = "MaximumMessageSize"; + static final String ATTR_MAXIMUM_DELIVERY_COUNT = "MaximumDeliveryCount"; static final String ATTR_DURABLE = "Durable"; static final String ATTR_AUTODELETE = "AutoDelete"; static final String ATTR_CONSUMER_COUNT = "ConsumerCount"; @@ -78,7 +80,8 @@ public interface ManagedQueue static final String ATTR_FLOW_OVERFULL = "FlowOverfull"; static final String ATTR_FLOW_RESUME_CAPACITY = "FlowResumeCapacity"; static final String ATTR_EXCLUSIVE = "Exclusive"; - + static final String ATTR_ALT_EXCHANGE = "AlternateExchange"; + //All attribute names constant static final List<String> QUEUE_ATTRIBUTES = Collections.unmodifiableList( @@ -91,6 +94,7 @@ public interface ManagedQueue ATTR_MAX_MSG_COUNT, ATTR_MAX_QUEUE_DEPTH, ATTR_MAX_MSG_SIZE, + ATTR_MAXIMUM_DELIVERY_COUNT, ATTR_DURABLE, ATTR_AUTODELETE, ATTR_CONSUMER_COUNT, @@ -101,7 +105,9 @@ public interface ManagedQueue ATTR_CAPACITY, ATTR_FLOW_OVERFULL, ATTR_FLOW_RESUME_CAPACITY, - ATTR_EXCLUSIVE)))); + ATTR_EXCLUSIVE, + ATTR_ALT_EXCHANGE + )))); /** * Returns the Name of the ManagedQueue. @@ -120,6 +126,16 @@ public interface ManagedQueue Integer getMessageCount() throws IOException; /** + * Maximum number of times a message is permitted to be delivered or zero if not enforced. + * + * @since Qpid JMX API 2.4 + * @return maximum delivery count + * @throws IOException + */ + @MBeanAttribute(name="MaximumDeliveryCount", description = "Maximum number of times a message is permitted to be delivered or zero if not enforced") + Integer getMaximumDeliveryCount() throws IOException; + + /** * Tells the total number of messages receieved by the queue since startup. * @return total number of messages received. * @throws IOException @@ -309,7 +325,7 @@ public interface ManagedQueue /** * Sets whether the queue is exclusive or not. - * + * * @since Qpid JMX API 2.0 * @param exclusive the capacity in bytes * @throws IOException @@ -318,6 +334,25 @@ public interface ManagedQueue @MBeanAttribute(name="Exclusive", description="Whether the queue is Exclusive or not") void setExclusive(boolean exclusive) throws IOException, JMException; + /** + * Sets the Alternate Exchange for the queue, for use in dead letter queue functionality. + * + * @since Qpid JMX API 2.4 + * @param the name of the exchange to use. Specifying null or the empty string will clear the alternate exchange. + * @throws IOException + */ + void setAlternateExchange(String exchangeName) throws IOException; + + /** + * Returns the name of the Alternate Exchange for the queue, or null if there isn't one. + * + * @since Qpid JMX API 2.4 + * @return the name of the Alternate Exchange for the queue, or null if there isn't one + * @throws IOException + */ + @MBeanAttribute(name="AlternateExchange", description="Alternate exchange for the queue") + String getAlternateExchange() throws IOException; + //********** Operations *****************// diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java index 12ae69571e..9d40edd8d0 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java @@ -47,7 +47,7 @@ public interface ServerInformation * Qpid JMX API 1.1 can be assumed. */ int QPID_JMX_API_MAJOR_VERSION = 2; - int QPID_JMX_API_MINOR_VERSION = 3; + int QPID_JMX_API_MINOR_VERSION = 4; /** diff --git a/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java b/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java index 1a4a73f207..c931b921df 100644 --- a/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java +++ b/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java @@ -23,7 +23,6 @@ package org.apache.qpid.management.common.mbeans; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.ArrayList; import java.util.List; import javax.management.MBeanAttributeInfo; diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java index 4a59176374..faa6769c63 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java @@ -47,7 +47,7 @@ public abstract class ApplicationRegistry //max supported broker management interface supported by this release of the management console public static final int SUPPORTED_QPID_JMX_API_MAJOR_VERSION = 2; - public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 3; + public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 4; public static final String DATA_DIR = System.getProperty("user.home") + File.separator + ".qpidmc"; diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java index 417734e5fb..cc2e4290a0 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java @@ -579,7 +579,7 @@ public class NavigationView extends ViewPart List<TreeObject> childNodes = typeNode.getChildren(); for (TreeObject child : childNodes) { - if (MBEAN.equals(child.getType()) && mbeanName.equals(child.getName())) + if (MBEAN.equals(child.getType()) && mbeanName != null && mbeanName.equals(child.getName())) { return true; } diff --git a/qpid/java/maven-settings.xml b/qpid/java/maven-settings.xml index 985f39b6f1..0f2e3e6156 100644 --- a/qpid/java/maven-settings.xml +++ b/qpid/java/maven-settings.xml @@ -1,2 +1,22 @@ +<!-- + - + - 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. + - + --> <settings> </settings> diff --git a/qpid/java/systests/etc/global-default.txt b/qpid/java/systests/etc/global-default.txt deleted file mode 100644 index 01b2c41809..0000000000 --- a/qpid/java/systests/etc/global-default.txt +++ /dev/null @@ -1,31 +0,0 @@ -# -# 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. -# - -CONFIG expand=true - -# This section grants the admin user access to all management methods -ACL ALLOW admin ALL METHOD - -# This section grants the client user access to all management methods except logging -ACL DENY client ALL METHOD component="LoggingManagement" -ACL ALLOW client ALL METHOD - -# This section grants the server user access to all management methods except configuration -ACL DENY server ALL METHOD component="ConfigurationManagement" -ACL ALLOW server ALL METHOD diff --git a/qpid/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt b/qpid/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt deleted file mode 100644 index a59b3176cb..0000000000 --- a/qpid/java/systests/etc/global-externaladminacl-changeloggerleveldenied.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# 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. -# - -# This section denies the admin user access to logging -ACL DENY admin UPDATE METHOD component="LoggingManagement" name="setRuntimeRootLoggerLevel" - -# This section grants the admin user access to management methods -ACL ALLOW admin ALL METHOD diff --git a/qpid/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt b/qpid/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt deleted file mode 100644 index ff024b5ee8..0000000000 --- a/qpid/java/systests/etc/global-externaladminacl-getallloggerlevelsdenied.txt +++ /dev/null @@ -1,25 +0,0 @@ -# -# 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. -# - -# This section denies the admin user access to logging methods -ACL DENY admin ACCESS METHOD component="LoggingManagement" name="getAvailableLoggerLevels" - -# This section grants the admin user access to all management methods -ACL ALLOW admin ALL METHOD - diff --git a/qpid/java/systests/etc/test-default.txt b/qpid/java/systests/etc/test-default.txt deleted file mode 100644 index 95e733d077..0000000000 --- a/qpid/java/systests/etc/test-default.txt +++ /dev/null @@ -1,73 +0,0 @@ -# -# 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. -# - -# This section grants virtualhost access rights -ACL ALLOW client ACCESS VIRTUALHOST -ACL ALLOW server ACCESS VIRTUALHOST - -# This section grants publish rights to an exchange + routing key pair - -# Allow clients to publish requests -ACL ALLOW client PUBLISH EXCHANGE name="amq.direct" routingKey="example.RequestQueue" - -# Allow the processor to respond to a client on their Temporary Topic -ACL ALLOW server PUBLISH EXCHANGE name="amq.direct" routingKey="tmp_*" -ACL ALLOW server PUBLISH EXCHANGE name="amq.direct" routingKey="TempQueue*" - -# This section grants users the ability to consume from the broker - -# Allow client to consume from temporary queues -ACL ALLOW client CONSUME QUEUE temporary=true - -# Only allow the server to consume from the Request Queue -ACL ALLOW server CONSUME QUEUE name="example.RequestQueue" - -# Allow client and server to consume from kipper queues -ACL ALLOW client CONSUME QUEUE name="clientid:kipper" -ACL ALLOW server CONSUME QUEUE name="clientid:kipper" - -# This section grants users the ability to create/delete queues and exchanges - -# Allow clients to create and delete temporary and kipper queue on this exchange -ACL ALLOW client CREATE QUEUE temporary=true -ACL ALLOW client DELETE QUEUE temporary=true -ACL ALLOW client CREATE QUEUE durable="true" -ACL ALLOW client DELETE QUEUE durable="true" - -# Allow the server to create the Request Queue and kipper queue -ACL ALLOW server CREATE QUEUE name="example.RequestQueue" -ACL ALLOW server CREATE QUEUE name="clientid:kipper" - -## Allow client and server exchange access for the relevant queues -ACL ALLOW client BIND EXCHANGE name="amq.direct" temporary=true -ACL ALLOW client UNBIND EXCHANGE name="amq.direct" temporary=true -ACL ALLOW client BIND EXCHANGE name="amq.direct" durable=true -ACL ALLOW client UNBIND EXCHANGE name="amq.direct" durable=true -ACL ALLOW server BIND EXCHANGE name="amq.direct" queueName="example.RequestQueue" - -## Allow client and server exchange access for the relevant topics -ACL ALLOW client BIND EXCHANGE name="amq.topic" durable=true routingKey=kipper -ACL ALLOW client UNBIND EXCHANGE name="amq.topic" durable=true routingKey=kipper -ACL ALLOW server BIND EXCHANGE name="amq.topic" durable=true routingKey=kipper - -# Action[operation=BIND,objectType=EXCHANGE,properties={OWNER=client, DURABLE=true, QUEUE_NAME=IllegalQueue, AUTO_DELETE=false, ROUTING_KEY=IllegalQueue, NAME=amq.direct, TEMPORARY=false, EXCLUSIVE=false}] - - -ACL ALLOW client CREATE EXCHANGE -ACL ALLOW server CREATE EXCHANGE diff --git a/qpid/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt b/qpid/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt deleted file mode 100644 index 197fe9dabe..0000000000 --- a/qpid/java/systests/etc/test-externalacljmx-deleteexchangefailure.txt +++ /dev/null @@ -1,26 +0,0 @@ -# -# 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. -# - -# This section grants virtualhost management rights -ACL ALLOW admin ALL METHOD - -# testDeleteExchangeFailure() -ACL ALLOW admin CREATE EXCHANGE name="amq.kipper.delete" -ACL DENY admin DELETE EXCHANGE name="amq.kipper.delete" - diff --git a/qpid/java/systests/etc/test-externalacljmx.txt b/qpid/java/systests/etc/test-externalacljmx.txt deleted file mode 100644 index f8a94bd44a..0000000000 --- a/qpid/java/systests/etc/test-externalacljmx.txt +++ /dev/null @@ -1,35 +0,0 @@ -# -# 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. -# - -# This section grants management access to the virtualhost -ACL ALLOW admin ALL METHOD -ACL ALLOW client ALL METHOD -ACL ALLOW server ALL METHOD - -# Allow create kipper queue -ACL ALLOW admin CREATE QUEUE name="kipper" owner = client # kipper -ACL ALLOW admin BIND EXCHANGE name="amq.direct" - -# testCreateExchangeSuccess(), testDeleteExchangeSuccess() -ACL ALLOW admin CREATE EXCHANGE name="amq.kipper.success" -ACL ALLOW admin DELETE EXCHANGE name="amq.kipper.success" - -# testCreateExchangeFailure() -ACL DENY admin CREATE EXCHANGE name="amq.kipper.failure" - diff --git a/qpid/java/systests/etc/test-logging.txt b/qpid/java/systests/etc/test-logging.txt deleted file mode 100644 index 76c6e442e0..0000000000 --- a/qpid/java/systests/etc/test-logging.txt +++ /dev/null @@ -1,23 +0,0 @@ -# -# 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. -# - -ACL ALLOW client CREATE QUEUE name="allow" -ACL ALLOW-LOG client CREATE QUEUE name="allow-log" -ACL DENY client CREATE QUEUE name="deny" -ACL DENY-LOG client CREATE QUEUE name="deny-log" diff --git a/qpid/java/systests/etc/test2-default.txt b/qpid/java/systests/etc/test2-default.txt deleted file mode 100644 index 0855e631d7..0000000000 --- a/qpid/java/systests/etc/test2-default.txt +++ /dev/null @@ -1,21 +0,0 @@ -# -# 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. -# - -# This section grants all access rights -ACL ALLOW guest ALL ALL
\ No newline at end of file diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/AddressBasedFailoverBehaviourTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/AddressBasedFailoverBehaviourTest.java index 980fc7285d..99fcbc5dc0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/AddressBasedFailoverBehaviourTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/AddressBasedFailoverBehaviourTest.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.client.failover; import javax.jms.Destination; diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java index a5b9c618bc..45aa6a0e18 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/failover/FailoverBehaviourTest.java @@ -56,7 +56,7 @@ public class FailoverBehaviourTest extends FailoverBaseCase implements Connectio private static boolean CLUSTERED = Boolean.getBoolean("profile.clustered"); /** Default number of messages to send before failover */ - private static final int DEFAULT_NUMBER_OF_MESSAGES = 10; + private static final int DEFAULT_NUMBER_OF_MESSAGES = 40; /** Actual number of messages to send before failover */ protected int _messageNumber = Integer.getInteger("profile.failoverMsgCount", DEFAULT_NUMBER_OF_MESSAGES); @@ -1157,7 +1157,6 @@ public class FailoverBehaviourTest extends FailoverBaseCase implements Connectio { init(acknowledgeMode, false); _consumer.close(); - QueueBrowser browser = _consumerSession.createBrowser((Queue) _destination); _connection.start(); produceMessages(TEST_MESSAGE_FORMAT, _messageNumber, false); @@ -1165,6 +1164,8 @@ public class FailoverBehaviourTest extends FailoverBaseCase implements Connectio { _producerSession.commit(); } + + QueueBrowser browser = _consumerSession.createBrowser((Queue) _destination); return browser; } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/prefetch/PrefetchBehaviourTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/prefetch/PrefetchBehaviourTest.java index c0b07f239b..d2950b095b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/prefetch/PrefetchBehaviourTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/prefetch/PrefetchBehaviourTest.java @@ -17,6 +17,26 @@ import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/* +* +* 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. +* +*/ public class PrefetchBehaviourTest extends QpidBrokerTestCase { protected static final Logger _logger = LoggerFactory.getLogger(PrefetchBehaviourTest.class); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/redelivered/RedeliveredMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/redelivered/RedeliveredMessageTest.java new file mode 100644 index 0000000000..a8fa183cbe --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/redelivered/RedeliveredMessageTest.java @@ -0,0 +1,50 @@ +package org.apache.qpid.client.redelivered; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class RedeliveredMessageTest extends QpidBrokerTestCase +{ + private Connection _connection; + + public void setUp() throws Exception + { + super.setUp(); + _connection = getConnection(); + } + + public void testRedeliveredFlagOnSessionClose() throws Exception + { + Session session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Destination destination = session.createQueue(getTestQueueName()); + MessageConsumer consumer = session.createConsumer(destination); + + final int numberOfMessages = 3; + sendMessage(session, destination, numberOfMessages); + + _connection.start(); + + for(int i = 0; i < numberOfMessages; i++) + { + final Message m = consumer.receive(1000l); + assertNotNull("Message is not recieved at " + i, m); + assertFalse("Redelivered should be not set", m.getJMSRedelivered()); + } + + session.close(); + session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + destination = session.createQueue(getTestQueueName()); + consumer = session.createConsumer(destination); + + for(int i = 0; i < numberOfMessages; i++) + { + final Message m = consumer.receive(1000l); + assertNotNull("Message is not recieved at " + i, m); + assertTrue("Redelivered should be set", m.getJMSRedelivered()); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedBrokerMBeanTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedBrokerMBeanTest.java new file mode 100644 index 0000000000..97ba9afc32 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedBrokerMBeanTest.java @@ -0,0 +1,166 @@ +/* + * 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.qpid.management.jmx; + +import java.util.Collections; +import java.util.Map; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.ObjectName; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedExchange; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Tests the JMX API for the Managed Broker. + * + */ +public class ManagedBrokerMBeanTest extends QpidBrokerTestCase +{ + /** + * Test virtual host + */ + private static final String VIRTUAL_HOST = "test"; + + /** + * Test exchange type + */ + private static final String EXCHANGE_TYPE = "topic"; + + /** + * JMX helper. + */ + private JMXTestUtils _jmxUtils; + private ManagedBroker _managedBroker; + + public void setUp() throws Exception + { + _jmxUtils = new JMXTestUtils(this); + _jmxUtils.setUp(); + super.setUp(); + _jmxUtils.open(); + _managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + } + + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + /** + * Tests queue creation/deletion also verifying the automatic binding to the default exchange. + */ + public void testCreateQueueAndDeletion() throws Exception + { + final String queueName = getTestQueueName(); + final ManagedExchange defaultExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString()); + + // Check that bind does not exist before queue creation + assertFalse("Binding to " + queueName + " should not exist in default exchange before queue creation", + defaultExchange.bindings().containsKey(new String[] {queueName})); + + _managedBroker.createNewQueue(queueName, "testowner", true); + + // Ensure the queue exists + assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName("test", queueName)); + assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName)); + + // Now verify that the default exchange has been bound. + assertTrue("Binding to " + queueName + " should exist in default exchange after queue creation", + defaultExchange.bindings().containsKey(new String[] {queueName})); + + // Now delete the queue + _managedBroker.deleteQueue(queueName); + + // Finally ensure that the binding has been removed. + assertFalse("Binding to " + queueName + " should not exist in default exchange after queue deletion", + defaultExchange.bindings().containsKey(new String[] {queueName})); + } + + /** + * Tests exchange creation/deletion via JMX API. + */ + public void testCreateExchangeAndUnregister() throws Exception + { + String exchangeName = getTestName(); + _managedBroker.createNewExchange(exchangeName, "topic", true); + String queryString = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=" + + ObjectName.quote(VIRTUAL_HOST) + ",name=" + ObjectName.quote(exchangeName) + ",ExchangeType=" + + EXCHANGE_TYPE; + ManagedExchange exchange = _jmxUtils.getManagedObject(ManagedExchange.class, queryString); + assertNotNull("Exchange should exist", exchange); + + _managedBroker.unregisterExchange(exchangeName); + assertFalse("Exchange should have been removed", _jmxUtils.isManagedObjectExist(queryString)); + } + + /** + * Tests that it is disallowed to unregister the default exchange. + */ + public void testUnregisterOfDefaultExchangeDisallowed() throws Exception + { + String defaultExchangeName = ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString(); + + try + { + _managedBroker.unregisterExchange(defaultExchangeName); + fail("Exception not thrown"); + } + catch (MBeanException mbe) + { + // PASS + assertEquals("Error in unregistering exchange " + defaultExchangeName, mbe.getMessage()); + assertTrue(mbe.getCause().getMessage().contains("Cannot unregister the default exchange")); + } + final ManagedExchange defaultExchange = _jmxUtils.getManagedExchange(defaultExchangeName); + assertNotNull("Exchange should exist", defaultExchange); + } + + /** + * Tests queue creation with {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument. Also tests + * that the attribute is exposed correctly through {@link ManagedQueue#getMaximumDeliveryCount()}. + */ + public void testCreateQueueWithMaximumDeliveryCountSet() throws Exception + { + final String queueName = getName(); + final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); + + final Integer deliveryCount = 1; + final Map<String, Object> args = Collections.singletonMap(AMQQueueFactory.X_QPID_MAXIMUM_DELIVERY_COUNT, (Object)deliveryCount); + managedBroker.createNewQueue(queueName, "testowner", true, args); + + // Ensure the queue exists + assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName("test", queueName)); + assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName)); + + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertEquals("Unexpected maximum delivery count", deliveryCount, managedQueue.getMaximumDeliveryCount()); + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedConnectionMBeanTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedConnectionMBeanTest.java new file mode 100644 index 0000000000..5f758061d5 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedConnectionMBeanTest.java @@ -0,0 +1,239 @@ +/* + * 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.qpid.management.jmx; + +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.management.JMException; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class ManagedConnectionMBeanTest extends QpidBrokerTestCase +{ + /** + * JMX helper. + */ + private JMXTestUtils _jmxUtils; + private Connection _connection; + + public void setUp() throws Exception + { + _jmxUtils = new JMXTestUtils(this); + _jmxUtils.setUp(); + super.setUp(); + _jmxUtils.open(); + _connection = getConnection(); + } + + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + public void testChannels() throws Exception + { + final String queueName = getTestQueueName(); + + final Session session = _connection.createSession(true, Session.SESSION_TRANSACTED); + final Destination destination = session.createQueue(queueName); + final MessageConsumer consumer = session.createConsumer(destination); + + final int numberOfMessages = 2; + sendMessage(session, destination, numberOfMessages); + _connection.start(); + + for (int i = 0; i < numberOfMessages; i++) + { + final Message m = consumer.receive(1000l); + assertNotNull("Message " + i + " is not received", m); + } + + List<ManagedConnection> connections = _jmxUtils.getManagedConnections("test"); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 1, connections.size()); + final ManagedConnection mBean = connections.get(0); + assertNotNull("Connection MBean is null", mBean); + + TabularData channelsData = mBean.channels(); + assertNotNull("Channels data are null", channelsData); + assertEquals("Unexpected number of rows in channel table", 1, channelsData.size()); + + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) channelsData.values().iterator(); + final CompositeDataSupport row = rowItr.next(); + Number unackCount = (Number) row.get(ManagedConnection.UNACKED_COUNT); + final Boolean transactional = (Boolean) row.get(ManagedConnection.TRANSACTIONAL); + final Boolean flowBlocked = (Boolean) row.get(ManagedConnection.FLOW_BLOCKED); + assertNotNull("Channel should have unacknowledged messages", unackCount); + assertEquals("Unexpected number of unacknowledged messages", 2, unackCount.intValue()); + assertNotNull("Channel should have transaction flag", transactional); + assertTrue("Unexpected transaction flag", transactional); + assertNotNull("Channel should have flow blocked flag", flowBlocked); + assertFalse("Unexpected value of flow blocked flag", flowBlocked); + + final Date initialLastIOTime = mBean.getLastIoTime(); + session.commit(); + assertTrue("Last IO time should have been updated", mBean.getLastIoTime().after(initialLastIOTime)); + + channelsData = mBean.channels(); + assertNotNull("Channels data are null", channelsData); + assertEquals("Unexpected number of rows in channel table", 1, channelsData.size()); + + final Iterator<CompositeDataSupport> rowItr2 = (Iterator<CompositeDataSupport>) channelsData.values().iterator(); + final CompositeDataSupport row2 = rowItr2.next(); + unackCount = (Number) row2.get(ManagedConnection.UNACKED_COUNT); + assertNotNull("Channel should have unacknowledged messages", unackCount); + assertEquals("Unexpected number of anacknowledged messages", 0, unackCount.intValue()); + + _connection.close(); + + connections = _jmxUtils.getManagedConnections("test"); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 0, connections.size()); + } + + public void testCommit() throws Exception + { + final String queueName = getTestQueueName(); + + final Session consumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final Session producerSession = _connection.createSession(true, Session.SESSION_TRANSACTED); + final Destination destination = producerSession.createQueue(queueName); + final MessageConsumer consumer = consumerSession.createConsumer(destination); + final MessageProducer producer = producerSession.createProducer(destination); + + _connection.start(); + + List<ManagedConnection> connections = _jmxUtils.getManagedConnections("test"); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 1, connections.size()); + final ManagedConnection mBean = connections.get(0); + assertNotNull("Connection MBean is null", mBean); + + final int numberOfMessages = 2; + for (int i = 0; i < numberOfMessages; i++) + { + producer.send(producerSession.createTextMessage("Test " + i)); + } + + // sync to make sure that messages are received on the broker + // before we commit via JMX + ((AMQSession<?, ?>) producerSession).sync(); + + Message m = consumer.receive(500l); + assertNull("Unexpected message received", m); + + Number channelId = getFirstTransactedChannelId(mBean, 2); + mBean.commitTransactions(channelId.intValue()); + + for (int i = 0; i < numberOfMessages; i++) + { + m = consumer.receive(1000l); + assertNotNull("Message " + i + " is not received", m); + assertEquals("Unexpected message received at " + i, "Test " + i, ((TextMessage) m).getText()); + } + producerSession.commit(); + m = consumer.receive(500l); + assertNull("Unexpected message received", m); + } + + protected Number getFirstTransactedChannelId(final ManagedConnection mBean, int channelNumber) throws IOException, JMException + { + TabularData channelsData = mBean.channels(); + assertNotNull("Channels data are null", channelsData); + assertEquals("Unexpected number of rows in channel table", channelNumber, channelsData.size()); + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) channelsData.values().iterator(); + while (rowItr.hasNext()) + { + final CompositeDataSupport row = rowItr.next(); + Boolean transacted = (Boolean) row.get(ManagedConnection.TRANSACTIONAL); + if (transacted.booleanValue()) + { + return (Number) row.get(ManagedConnection.CHAN_ID); + } + } + return null; + } + + public void testRollback() throws Exception + { + final String queueName = getTestQueueName(); + + final Session consumerSession = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final Session producerSession = _connection.createSession(true, Session.SESSION_TRANSACTED); + final Destination destination = producerSession.createQueue(queueName); + final MessageConsumer consumer = consumerSession.createConsumer(destination); + final MessageProducer producer = producerSession.createProducer(destination); + + List<ManagedConnection> connections = _jmxUtils.getManagedConnections("test"); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 1, connections.size()); + final ManagedConnection mBean = connections.get(0); + assertNotNull("Connection MBean is null", mBean); + + final int numberOfMessages = 2; + for (int i = 0; i < numberOfMessages; i++) + { + producer.send(producerSession.createTextMessage("Test " + i)); + } + + // sync to make sure that messages are received on the broker + // before we rollback via JMX + ((AMQSession<?, ?>) producerSession).sync(); + + Number channelId = getFirstTransactedChannelId(mBean, 2); + mBean.rollbackTransactions(channelId.intValue()); + + Message m = consumer.receive(1000l); + assertNull("Unexpected message received", m); + + producerSession.commit(); + + _connection.start(); + m = consumer.receive(1000l); + assertNull("Unexpected message received", m); + } + + public void testAuthorisedId() throws Exception + { + List<ManagedConnection> connections = _jmxUtils.getManagedConnections("test"); + assertNotNull("Connection MBean is not found", connections); + assertEquals("Unexpected number of connection mbeans", 1, connections.size()); + final ManagedConnection mBean = connections.get(0); + assertNotNull("Connection MBean is null", mBean); + assertEquals("Unexpected authorized id", "guest", mBean.getAuthorizedId()); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedQueueMBeanTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedQueueMBeanTest.java new file mode 100644 index 0000000000..0e60cc5d8f --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedQueueMBeanTest.java @@ -0,0 +1,129 @@ +/* + * 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.qpid.management.jmx; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.Connection; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; + +import org.apache.commons.lang.time.FastDateFormat; +import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.server.queue.AMQQueueMBean; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Tests the JMX API for the Managed Queue. + * + */ +public class ManagedQueueMBeanTest extends QpidBrokerTestCase +{ + /** + * JMX helper. + */ + private JMXTestUtils _jmxUtils; + + public void setUp() throws Exception + { + _jmxUtils = new JMXTestUtils(this); + _jmxUtils.setUp(); + super.setUp(); + _jmxUtils.open(); + } + + public void tearDown() throws Exception + { + if (_jmxUtils != null) + { + _jmxUtils.close(); + } + super.tearDown(); + } + + /** + * Tests {@link ManagedQueue#viewMessages(long, long)} interface. + */ + public void testViewSingleMessage() throws Exception + { + final String queueName = getTestQueueName(); + + // Create queue and send numMessages messages to it. + final Connection con = getConnection(); + final Session session = con.createSession(true, Session.SESSION_TRANSACTED); + final Destination dest = session.createQueue(queueName); + session.createConsumer(dest).close(); // Create a consumer only to cause queue creation + + final List<Message> sentMessages = sendMessage(session, dest, 1); + final Message sentMessage = sentMessages.get(0); + + // Obtain the management interface. + final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName); + assertNotNull("ManagedQueue expected to be available", managedQueue); + assertEquals("Unexpected queue depth", 1, managedQueue.getMessageCount().intValue()); + + // Check the contents of the message + final TabularData tab = managedQueue.viewMessages(1l, 1l); + assertEquals("Unexpected number of rows in table", 1, tab.size()); + final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) tab.values().iterator(); + + final CompositeDataSupport row1 = rowItr.next(); + assertNotNull("Message should have AMQ message id", row1.get(ManagedQueue.MSG_AMQ_ID)); + assertEquals("Unexpected queue position", 1l, row1.get(ManagedQueue.MSG_QUEUE_POS)); + assertEquals("Unexpected redelivered flag", Boolean.FALSE, row1.get(ManagedQueue.MSG_REDELIVERED)); + + // Check the contents of header (encoded in a string array) + final String[] headerArray = (String[]) row1.get(ManagedQueue.MSG_HEADER); + assertNotNull("Expected message header array", headerArray); + final Map<String, String> headers = headerArrayToMap(headerArray); + + final String expectedJMSMessageID = isBroker010() ? sentMessage.getJMSMessageID().replace("ID:", "") : sentMessage.getJMSMessageID(); + final String expectedFormattedJMSTimestamp = FastDateFormat.getInstance(AMQQueueMBean.JMSTIMESTAMP_DATETIME_FORMAT).format(sentMessage.getJMSTimestamp()); + assertEquals("Unexpected JMSMessageID within header", expectedJMSMessageID, headers.get("JMSMessageID")); + assertEquals("Unexpected JMSPriority within header", String.valueOf(sentMessage.getJMSPriority()), headers.get("JMSPriority")); + assertEquals("Unexpected JMSTimestamp within header", expectedFormattedJMSTimestamp, headers.get("JMSTimestamp")); + } + + /** + * + * Utility method to convert array of Strings in the form x = y into a + * map with key/value x => y. + * + */ + private Map<String,String> headerArrayToMap(final String[] headerArray) + { + final Map<String, String> headerMap = new HashMap<String, String>(); + final List<String> headerList = Arrays.asList(headerArray); + for (Iterator<String> iterator = headerList.iterator(); iterator.hasNext();) + { + final String nameValuePair = iterator.next(); + final String[] nameValue = nameValuePair.split(" *= *", 2); + headerMap.put(nameValue[0], nameValue[1]); + } + return headerMap; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java index 12a1682212..0e2875235f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java @@ -45,12 +45,11 @@ public class ManagementActorLoggingTest extends AbstractTestLogging { private JMXTestUtils _jmxUtils; private boolean _closed = false; - private static final String USER = "admin"; @Override public void setUp() throws Exception { - _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils = new JMXTestUtils(this); _jmxUtils.setUp(); super.setUp(); _jmxUtils.open(); @@ -364,7 +363,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging List<String> results = waitAndFindMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("Unexpected number of bindings logged", 2, results.size()); String log = getLogMessage(results, 0); @@ -391,7 +390,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging List<String> results = waitAndFindMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("Unexpected number of bindings logged", 2, results.size()); String log = getLogMessage(results, 0); @@ -418,7 +417,7 @@ public class ManagementActorLoggingTest extends AbstractTestLogging List<String> results = waitAndFindMatches("BND-1001"); - assertEquals("More than one bind creation found", 1, results.size()); + assertEquals("Unexpected number of bindings logged", 2, results.size()); String log = getLogMessage(results, 0); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java deleted file mode 100644 index fd33266414..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/MessageDisappearWithIOExceptionTest.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * - * 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.qpid.server.failover; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.protocol.AMQProtocolSession; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.test.utils.FailoverBaseCase; - -import javax.jms.Destination; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Test case based on user reported error. - * - * Summary: - * A user has reported message loss from their application. On bouncing of - * the broker the 'lost' messages are delivered to the broker. - * - * Note: - * The client was using Spring so that may influence the situation. - * - * Issue: - * The log files show 7 instances of the following which result in 7 - * missing messages. - * - * The client log files show: - * - * The broker log file show: - * - * - * 7 missing messages have delivery tags 5-11. Which says that they are - * sequentially the next message from the broker. - * - * The only way for the 'without a handler' log to occur is if the consumer - * has been removed from the look up table of the dispatcher. - * And the only way for the 'null message' log to occur on the broker is is - * if the message does not exist in the unacked-map - * - * The consumer is only removed from the list during session - * closure and failover. - * - * If the session was closed then the broker would requeue the unacked - * messages so the potential exists to have an empty map but the broker - * will not send a message out after the unacked map has been cleared. - * - * When failover occurs the _consumer map is cleared and the consumers are - * resubscribed. This is down without first stopping any existing - * dispatcher so there exists the potential to receive a message after - * the _consumer map has been cleared which is how the 'without a handler' - * log statement occurs. - * - * Scenario: - * - * Looking over logs the sequence that best fits the events is as follows: - * - Something causes Mina to be delayed causing the WriteTimoutException. - * - This exception is recevied by AMQProtocolHandler#exceptionCaught - * - As the WriteTimeoutException is an IOException this will cause - * sessionClosed to be called to start failover. - * + This is potentially the issues here. All IOExceptions are treated - * as connection failure events. - * - Failover Runs - * + Failover assumes that the previous connection has been closed. - * + Failover binds the existing objects (AMQConnection/Session) to the - * new connection objects. - * - Everything is reported as being successfully failed over. - * However, what is neglected is that the original connection has not - * been closed. - * + So what occurs is that the broker sends a message to the consumer on - * the original connection, as it was not notified of the client - * failing over. - * As the client failover reuses the original AMQSession and Dispatcher - * the new messages the broker sends to the old consumer arrives at the - * client and is processed by the same AMQSession and Dispatcher. - * However, as the failover process cleared the _consumer map and - * resubscribe the consumers the Dispatcher does not recognise the - * delivery tag and so logs the 'without a handler' message. - * - The Dispatcher then attempts to reject the message, however, - * + The AMQSession/Dispatcher pair have been swapped to using a new Mina - * ProtocolSession as part of the failover process so the reject is - * sent down the second connection. The broker receives the Reject - * request but as the Message was sent on a different connection the - * unacknowledgemap is empty and a 'message is null' log message - * produced. - * - * Test Strategy: - * - * It should be easy to demonstrate if we can send an IOException to - * AMQProtocolHandler#exceptionCaught and then try sending a message. - * - * The current unknowns here are the type of consumers that are in use. - * If it was an exclusive queue(Durable Subscription) then why did the - * resubscribe not fail. - * - * If it was not exclusive then why did the messages not round robin? - */ -public class MessageDisappearWithIOExceptionTest extends FailoverBaseCase implements ConnectionListener -{ - private CountDownLatch _failoverOccured = new CountDownLatch(1); - AMQConnection _connection; - Session _session; - Queue _queue; - MessageConsumer _consumer; - - public void setUp() throws Exception - { - super.setUp(); - stopBroker(getFailingPort()); - - } - - /** - * Test Summary: - * - * Create a queue consumer and send 10 messages to the broker. - * - * Consume the first message. - * This will pull the rest into the prefetch - * - * Send an IOException to the MinaProtocolHandler. - * - * This will force failover to occur. - * - * 9 messages would normally be expected but it is expected that none will - * arrive. As they are still in the prefetch of the first session. - * - * To free the messages we need to close all connections. - * - Simply doing connection.close() and retesting will not be enough as - * the original connection's IO layer will still exist and is nolonger - * connected to the connection object as a result of failover. - * - * - Test will need to retain a reference to the original connection IO so - * that it can be closed releasing the messages to validate that the - * messages have indeed been 'lost' on that sesssion. - */ - public void test() throws Exception - { - initialiseConnection(); - - // Create Producer - // Send 10 messages - List<Message> messages = sendNumberedBytesMessage(_session, _queue, 10); - - // Consume first messasge - Message received = _consumer.receive(2000); - - // Verify received messages - assertNotNull("First message not received.", received); - assertEquals("Incorrect message Received", - messages.remove(0).getIntProperty("count"), - received.getIntProperty("count")); - - // When the Exception is received by the underlying IO layer it will - // initiate failover. The first step of which is to ensure that the - // existing conection is closed. So in this situation the connection - // will be flushed casuing the above ACK to be sent to the broker. - // - // That said: - // when the socket close is detected on the server it will rise up the - // Mina filter chain and interrupt processing. - // this has been raised as QPID-2138 - _session.createConsumer(_session.createTemporaryQueue()).close(); - - //Retain IO Layer - AMQProtocolSession protocolSession = _connection.getProtocolHandler().getProtocolSession(); - - // Send IO Exception - causing failover - _connection.getProtocolHandler(). - exception(new IOException("IOException to cause failover.")); - - // Verify Failover occured through ConnectionListener - assertTrue("Failover did not occur", - _failoverOccured.await(4000, TimeUnit.MILLISECONDS)); - - /***********************************/ - // This verifies that the bug has been resolved - - // Attempt to consume again. Expect 9 messages - for (int count = 1; count < 10; count++) - { - received = _consumer.receive(2000); - assertNotNull("Expected message not received:" + count, received); - assertEquals(messages.remove(0).getIntProperty("count"), - received.getIntProperty("count")); - } - - //Verify there are no more messages - received = _consumer.receive(1000); - assertNull("Message receieved when there should be none:" + received, - received); - -// /***********************************/ -// // This verifies that the bug exists -// -// // Attempt to consume remaining 9 messages.. Expecting NONE. -// // receiving just one message should fail so no need to fail 9 times -// received = _consumer.receive(1000); -// assertNull("Message receieved when it should be null:" + received, received); -// -//// //Close the Connection which you would assume would free the messages -//// _connection.close(); -//// -//// // Reconnect -//// initialiseConnection(); -//// -//// // We should still be unable to receive messages -//// received = _consumer.receive(1000); -//// assertNull("Message receieved when it should be null:" + received, received); -//// -//// _connection.close(); -// -// // Close original IO layer. Expecting messages to be released -// protocolSession.closeProtocolSession(); -// -// // Reconnect and all should be good. -//// initialiseConnection(); -// -// // Attempt to consume again. Expect 9 messages -// for (int count = 1; count < 10; count++) -// { -// received = _consumer.receive(2000); -// assertNotNull("Expected message not received:" + count, received); -// assertEquals(messages.remove(0).getIntProperty("count"), -// received.getIntProperty("count")); -// } -// -// //Verify there are no more messages -// received = _consumer.receive(1000); -// assertNull("Message receieved when there should be none:" + received, -// received); - } - - private void initialiseConnection() - throws Exception - { - //Create Connection using the default connection URL. i.e. not the Failover URL that would be used by default - _connection = (AMQConnection) getConnectionFactory("default").createConnection("guest", "guest"); - // The default connection does not have any retries configured so - // Allow this connection to retry so that we can block on the failover. - // The alternative would be to use the getConnection() default. However, - // this would add additional complexity in the logging as a second - // broker is defined in that url. We do not need it for this test. - _connection.getFailoverPolicy().getCurrentMethod().setRetries(1); - _connection.setConnectionListener(this); - - _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - _queue = _session.createQueue(getTestQueueName()); - - // Create Consumer - _consumer = _session.createConsumer(_queue); - - //Start connection - _connection.start(); - } - - /** QpidTestCase back port to this release */ - - // modified from QTC as sendMessage is not testable. - // - should be renamed sendBlankBytesMessage - // - should be renamed sendNumberedBytesMessage - public List<Message> sendNumberedBytesMessage(Session session, Destination destination, - int count) throws Exception - { - List<Message> messages = new ArrayList<Message>(count); - - MessageProducer producer = session.createProducer(destination); - - for (int i = 0; i < count; i++) - { - Message next = session.createMessage(); - - next.setIntProperty("count", i); - - producer.send(next); - - messages.add(next); - } - - producer.close(); - return messages; - } - - public void bytesSent(long count) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void bytesReceived(long count) - { - } - - public boolean preFailover(boolean redirect) - { - //Allow failover to occur - return true; - } - - public boolean preResubscribe() - { - //Allow failover to occur - return true; - } - - public void failoverComplete() - { - _failoverOccured.countDown(); - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java index 2629e82831..e7da4472f5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java @@ -18,7 +18,6 @@ */ package org.apache.qpid.server.logging; -import java.io.File; import java.util.List; import javax.jms.Connection; @@ -29,6 +28,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQSession; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; /** * ACL version 2/3 file testing to verify that ACL actor logging works correctly. @@ -49,13 +49,18 @@ public class AccessControlLoggingTest extends AbstractTestLogging public void setUp() throws Exception { - setConfigurationProperty("virtualhosts.virtualhost.test.security.aclv2", - QpidHome + File.separator + "etc" + File.separator + "test-logging.txt"); - + // Write out ACL for this test + AbstractACLTestCase.writeACLFileUtil(this, "test", + "ACL ALLOW client ACCESS VIRTUALHOST", + "ACL ALLOW client CREATE QUEUE name='allow'", + "ACL ALLOW-LOG client CREATE QUEUE name='allow-log'", + "ACL DENY client CREATE QUEUE name='deny'", + "ACL DENY-LOG client CREATE QUEUE name='deny-log'"); + super.setUp(); + } - /** FIXME This comes from SimpleACLTest and makes me suspicious. */ @Override public void tearDown() throws Exception { @@ -69,7 +74,7 @@ public class AccessControlLoggingTest extends AbstractTestLogging //that we provoked with authentication failures, where the test passes - we can ignore on con close } } - + /** * Test that {@code allow} ACL entries do not log anything. */ diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java index 24e6aa4207..ed9109ebba 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java @@ -20,9 +20,9 @@ */ package org.apache.qpid.server.logging; -import junit.framework.AssertionFailedError; import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.test.utils.JMXTestUtils; import org.apache.qpid.util.LogMonitor; import java.util.List; @@ -41,6 +41,8 @@ import java.io.File; * MNG-1004 : Ready * MNG-1005 : Stopped * MNG-1006 : Using SSL Keystore : <path> + * MNG-1007 : Open : User <username> + * MNG-1008 : Close : User <username> */ public class ManagementLoggingTest extends AbstractTestLogging { @@ -86,33 +88,24 @@ public class ManagementLoggingTest extends AbstractTestLogging waitForMessage("MNG-1001"); List<String> results = findMatches(MNG_PREFIX); - - try - { - // Validation + // Validation - assertTrue("MNGer message not logged", results.size() > 0); + assertTrue("MNGer message not logged", results.size() > 0); - String log = getLogMessage(results, 0); + String log = getLogMessage(results, 0); - //1 - validateMessageID("MNG-1001", log); + //1 + validateMessageID("MNG-1001", log); - //2 - //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) - results = findMatches("MNG-1001"); - assertEquals("Unexpected startup message count.", - 2, results.size()); + //2 + //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J) + results = findMatches("MNG-1001"); + assertEquals("Unexpected startup message count.", + 2, results.size()); - //3 - assertEquals("Startup log message is not 'Startup'.", "Startup", - getMessageString(log)); - } - catch (AssertionFailedError afe) - { - dumpLogs(results, _monitor); - throw afe; - } + //3 + assertEquals("Startup log message is not 'Startup'.", "Startup", + getMessageString(log)); } } @@ -135,17 +128,9 @@ public class ManagementLoggingTest extends AbstractTestLogging startBrokerAndCreateMonitor(false, false); List<String> results = findMatches(MNG_PREFIX); - try - { - // Validation + // Validation - assertEquals("MNGer messages logged", 0, results.size()); - } - catch (AssertionFailedError afe) - { - dumpLogs(results, _monitor); - throw afe; - } + assertEquals("MNGer messages logged", 0, results.size()); } } @@ -194,39 +179,31 @@ public class ManagementLoggingTest extends AbstractTestLogging startBrokerAndCreateMonitor(true, false); List<String> results = waitAndFindMatches("MNG-1002"); - try - { - // Validation + // Validation - //There will be 4 startup messages (two via SystemOut, and two via Log4J) - assertEquals("Unexpected MNG-1002 message count", 4, results.size()); + //There will be 4 startup messages (two via SystemOut, and two via Log4J) + assertEquals("Unexpected MNG-1002 message count", 4, results.size()); - String log = getLogMessage(results, 0); + String log = getLogMessage(results, 0); - //1 - validateMessageID("MNG-1002", log); + //1 + validateMessageID("MNG-1002", log); - //Check the RMI Registry port is as expected - int mPort = getManagementPort(getPort()); - assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log), - getMessageString(log).endsWith(String.valueOf(mPort))); + //Check the RMI Registry port is as expected + int mPort = getManagementPort(getPort()); + assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log), + getMessageString(log).endsWith(String.valueOf(mPort))); - log = getLogMessage(results, 2); + log = getLogMessage(results, 2); - //1 - validateMessageID("MNG-1002", log); + //1 + validateMessageID("MNG-1002", log); - // We expect the RMI Registry port (the defined 'management port') to be - // 100 lower than the JMX RMIConnector Server Port (the actual JMX server) - int jmxPort = mPort + ServerConfiguration.JMXPORT_CONNECTORSERVER_OFFSET; - assertTrue("JMX RMIConnectorServer port not as expected(" + jmxPort + ").:" + getMessageString(log), - getMessageString(log).endsWith(String.valueOf(jmxPort))); - } - catch (AssertionFailedError afe) - { - dumpLogs(results, _monitor); - throw afe; - } + // We expect the RMI Registry port (the defined 'management port') to be + // 100 lower than the JMX RMIConnector Server Port (the actual JMX server) + int jmxPort = mPort + ServerConfiguration.JMXPORT_CONNECTORSERVER_OFFSET; + assertTrue("JMX RMIConnectorServer port not as expected(" + jmxPort + ").:" + getMessageString(log), + getMessageString(log).endsWith(String.valueOf(jmxPort))); } } @@ -251,35 +228,75 @@ public class ManagementLoggingTest extends AbstractTestLogging startBrokerAndCreateMonitor(true, true); List<String> results = waitAndFindMatches("MNG-1006"); - try - { - // Validation - assertTrue("MNGer message not logged", results.size() > 0); + assertTrue("MNGer message not logged", results.size() > 0); + + String log = getLogMessage(results, 0); - String log = getLogMessage(results, 0); + //1 + validateMessageID("MNG-1006", log); - //1 - validateMessageID("MNG-1006", log); + // Validate we only have two MNG-1002 (one via stdout, one via log4j) + results = findMatches("MNG-1006"); + assertEquals("Upexpected SSL Keystore message count", + 2, results.size()); + + // Validate the keystore path is as expected + assertTrue("SSL Keystore entry expected.:" + getMessageString(log), + getMessageString(log).endsWith(new File(getConfigurationStringProperty("management.ssl.keyStorePath")).getName())); + } + } - // Validate we only have two MNG-1002 (one via stdout, one via log4j) - results = findMatches("MNG-1006"); - assertEquals("Upexpected SSL Keystore message count", - 2, results.size()); + /** + * Description: Tests the management connection open/close are logged correctly. + * + * Output: + * + * <date> MESSAGE MNG-1007 : Open : User <username> + * <date> MESSAGE MNG-1008 : Close : User <username> + * + * Validation Steps: + * + * 1. The MNG ID is correct + * 2. The message and username are correct + */ + public void testManagementUserOpenClose() throws Exception + { + if (isJavaBroker()) + { + startBrokerAndCreateMonitor(true, false); - // Validate the keystore path is as expected - assertTrue("SSL Keystore entry expected.:" + getMessageString(log), - getMessageString(log).endsWith(new File(getConfigurationStringProperty("management.ssl.keyStorePath")).getName())); + final JMXTestUtils jmxUtils = new JMXTestUtils(this); + List<String> openResults = null; + List<String> closeResults = null; + try + { + jmxUtils.setUp(); + jmxUtils.open(); + openResults = waitAndFindMatches("MNG-1007"); } - catch (AssertionFailedError afe) + finally { - dumpLogs(results, _monitor); - throw afe; + if (jmxUtils != null) + { + jmxUtils.close(); + closeResults = waitAndFindMatches("MNG-1008"); + } } - } + assertNotNull("Management Open results null", openResults.size()); + assertEquals("Management Open logged unexpected number of times", 1, openResults.size()); + + assertNotNull("Management Close results null", closeResults.size()); + assertEquals("Management Close logged unexpected number of times", 1, closeResults.size()); + + final String openMessage = getMessageString(getLogMessage(openResults, 0)); + assertTrue("Unexpected open message " + openMessage, openMessage.endsWith("Open : User admin")); + final String closeMessage = getMessageString(getLogMessage(closeResults, 0)); + assertTrue("Unexpected close message " + closeMessage, closeMessage.endsWith("Close : User admin")); + } } - + private void startBrokerAndCreateMonitor(boolean managementEnabled, boolean useManagementSSL) throws Exception { //Ensure management is on diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java index e3fd042560..ddc51f69bd 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java @@ -60,7 +60,6 @@ import java.lang.reflect.UndeclaredThrowableException; public class ModelTest extends QpidBrokerTestCase { - private static final String USER = "admin"; private JMXTestUtils _jmxUtils; private static final String VIRTUALHOST_NAME = "test"; @@ -68,7 +67,7 @@ public class ModelTest extends QpidBrokerTestCase public void setUp() throws Exception { // Create a JMX Helper - _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils = new JMXTestUtils(this); _jmxUtils.setUp(); super.setUp(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityQueueTest.java index 2ce1251eab..962aec0d1e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityQueueTest.java @@ -20,35 +20,26 @@ */ package org.apache.qpid.server.queue; -import junit.framework.TestCase; -import junit.framework.Assert; -import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.client.AMQQueue; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; -import org.apache.qpid.test.utils.QpidBrokerTestCase; -import org.apache.qpid.url.URLSyntaxException; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; - -import javax.jms.*; -import javax.naming.NamingException; -import javax.naming.Context; -import javax.naming.spi.InitialContextFactory; -import java.util.Hashtable; import java.util.HashMap; import java.util.Map; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.naming.NamingException; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.test.utils.QpidBrokerTestCase; -public class PriorityTest extends QpidBrokerTestCase +public class PriorityQueueTest extends QpidBrokerTestCase { private static final int TIMEOUT = 1500; - - private static final Logger _logger = Logger.getLogger(PriorityTest.class); - protected final String QUEUE = "PriorityQueue"; private static final int MSG_COUNT = 50; @@ -60,9 +51,8 @@ public class PriorityTest extends QpidBrokerTestCase private Connection consumerConnection; private Session consumerSession; - private MessageConsumer consumer; - + protected void setUp() throws Exception { super.setUp(); @@ -71,10 +61,10 @@ public class PriorityTest extends QpidBrokerTestCase producerSession = producerConnection.createSession(true, Session.AUTO_ACKNOWLEDGE); producerConnection.start(); - + consumerConnection = getConnection(); consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); - + } protected void tearDown() throws Exception @@ -111,7 +101,7 @@ public class PriorityTest extends QpidBrokerTestCase Message previous = null; int messageCount = 0; while((received = consumer.receive(1000))!=null) - { + { messageCount++; if(previous != null) { @@ -124,17 +114,17 @@ public class PriorityTest extends QpidBrokerTestCase assertEquals("Incorrect number of message received", 50, receivedCount); } - + public void testOddOrdering() throws AMQException, JMSException { final Map<String,Object> arguments = new HashMap<String, Object>(); arguments.put("x-qpid-priorities",3); ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); queue = producerSession.createQueue("direct://amq.direct/"+QUEUE+"/"+QUEUE+"?durable='false'&autodelete='true'"); - + ((AMQSession) producerSession).declareAndBind((AMQDestination)queue); producer = producerSession.createProducer(queue); - + // In order ABC producer.setPriority(9); producer.send(nextMessage(1, false, producerSession, producer)); @@ -151,14 +141,14 @@ public class PriorityTest extends QpidBrokerTestCase producer.setPriority(1); producer.send(nextMessage(6, false, producerSession, producer)); - // Out of order BCA + // Out of order BCA producer.setPriority(4); producer.send(nextMessage(7, false, producerSession, producer)); producer.setPriority(1); producer.send(nextMessage(8, false, producerSession, producer)); producer.setPriority(9); producer.send(nextMessage(9, false, producerSession, producer)); - + // Reverse order CBA producer.setPriority(1); producer.send(nextMessage(10, false, producerSession, producer)); @@ -167,10 +157,10 @@ public class PriorityTest extends QpidBrokerTestCase producer.setPriority(9); producer.send(nextMessage(12, false, producerSession, producer)); producerSession.commit(); - + consumer = consumerSession.createConsumer(queue); consumerConnection.start(); - + Message msg = consumer.receive(TIMEOUT); assertEquals(1, msg.getIntProperty("msg")); msg = consumer.receive(TIMEOUT); @@ -179,7 +169,7 @@ public class PriorityTest extends QpidBrokerTestCase assertEquals(9, msg.getIntProperty("msg")); msg = consumer.receive(TIMEOUT); assertEquals(12, msg.getIntProperty("msg")); - + msg = consumer.receive(TIMEOUT); assertEquals(2, msg.getIntProperty("msg")); msg = consumer.receive(TIMEOUT); @@ -188,7 +178,7 @@ public class PriorityTest extends QpidBrokerTestCase assertEquals(7, msg.getIntProperty("msg")); msg = consumer.receive(TIMEOUT); assertEquals(11, msg.getIntProperty("msg")); - + msg = consumer.receive(TIMEOUT); assertEquals(3, msg.getIntProperty("msg")); msg = consumer.receive(TIMEOUT); @@ -206,6 +196,4 @@ public class PriorityTest extends QpidBrokerTestCase return send; } - - } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java index a724e6c66e..775d2c3eb0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java @@ -55,11 +55,10 @@ public class ProducerFlowControlTest extends AbstractTestLogging private JMXTestUtils _jmxUtils; private boolean _jmxUtilConnected; - private static final String USER = "admin"; public void setUp() throws Exception { - _jmxUtils = new JMXTestUtils(this, USER , USER); + _jmxUtils = new JMXTestUtils(this); _jmxUtils.setUp(); _jmxUtilConnected=false; super.setUp(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SortedQueueTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SortedQueueTest.java new file mode 100644 index 0000000000..bf4dbcb19f --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SortedQueueTest.java @@ -0,0 +1,523 @@ +/* + * 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.qpid.server.queue; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.NamingException; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.configuration.ClientProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +public class SortedQueueTest extends QpidBrokerTestCase +{ + private static final Logger LOGGER = Logger.getLogger(SortedQueueTest.class); + public static final String TEST_SORT_KEY = "testSortKey"; + private static final String VALUES[] = SortedQueueEntryListTest.keys.clone(); + private static final String VALUES_SORTED[] = SortedQueueEntryListTest.keys.clone(); + public final static String SUBSET_KEYS[] = { "000", "100", "200", "300", "400", "500", "600", "700", "800", "900" }; + + private Connection _producerConnection; + private Session _producerSession; + private Connection _consumerConnection; + + protected void setUp() throws Exception + { + super.setUp(); + setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, "1"); + // Sort value array to generated "expected" order of messages. + Arrays.sort(VALUES_SORTED); + _producerConnection = getConnection(); + _consumerConnection = getConnection(); + _producerSession = _producerConnection.createSession(true, Session.SESSION_TRANSACTED); + } + + protected void tearDown() throws Exception + { + _producerSession.close(); + _producerConnection.close(); + _consumerConnection.close(); + super.tearDown(); + } + + public void testSortOrder() throws JMSException, NamingException, AMQException + { + final Queue queue = createQueue(); + final MessageProducer producer = _producerSession.createProducer(queue); + + for(String value : VALUES) + { + final Message msg = _producerSession.createTextMessage("Message Text:" + value); + msg.setStringProperty(TEST_SORT_KEY, value); + producer.send(msg); + } + + _producerSession.commit(); + producer.close(); + + final Session consumerSession = _consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final MessageConsumer consumer = consumerSession.createConsumer(queue); + _consumerConnection.start(); + TextMessage received; + int messageCount = 0; + while((received = (TextMessage) consumer.receive(1000)) != null) + { + assertEquals("Received message with unexpected sorted key value", VALUES_SORTED[messageCount], + received.getStringProperty(TEST_SORT_KEY)); + assertEquals("Received message with unexpected message value", + "Message Text:" + VALUES_SORTED[messageCount], received.getText()); + messageCount++; + } + + assertEquals("Incorrect number of messages received", VALUES.length, messageCount); + } + + public void testAutoAckSortedQueue() throws JMSException, NamingException, AMQException + { + runThroughSortedQueueForSessionMode(Session.AUTO_ACKNOWLEDGE); + } + + public void testTransactedSortedQueue() throws JMSException, NamingException, AMQException + { + runThroughSortedQueueForSessionMode(Session.SESSION_TRANSACTED); + } + + public void testClientAckSortedQueue() throws JMSException, NamingException, AMQException + { + runThroughSortedQueueForSessionMode(Session.CLIENT_ACKNOWLEDGE); + } + + private void runThroughSortedQueueForSessionMode(final int sessionMode) throws JMSException, NamingException, + AMQException + { + final Queue queue = createQueue(); + final MessageProducer producer = _producerSession.createProducer(queue); + + final TestConsumerThread consumerThread = new TestConsumerThread(sessionMode, queue); + consumerThread.start(); + final Calendar cal = Calendar.getInstance(Locale.UK); + + for(String value : VALUES) + { + final Message msg = _producerSession.createTextMessage(String.valueOf(cal.getTimeInMillis())); + msg.setStringProperty(TEST_SORT_KEY, value); + producer.send(msg); + _producerSession.commit(); + } + + synchronized(consumerThread) + { + try + { + consumerThread.join(5000L); + } + catch(InterruptedException e) + { + fail("Test failed waiting for consumer to complete"); + } + } + assertTrue("Consumer timed out", consumerThread.isStopped()); + assertEquals("Incorrect number of messages received", VALUES.length, consumerThread.getConsumed()); + + producer.close(); + } + + public void testSortedQueueWithAscendingSortedKeys() throws JMSException, NamingException, AMQException + { + final Queue queue = createQueue(); + final MessageProducer producer = _producerSession.createProducer(queue); + + final TestConsumerThread consumerThread = new TestConsumerThread(Session.AUTO_ACKNOWLEDGE, queue); + consumerThread.start(); + + for(int i = 0; i < 200; i++) + { + final String ascendingKey = AscendingSortedKeys.getNextKey(); + final Message msg = _producerSession.createTextMessage("Message Text:" + ascendingKey); + msg.setStringProperty(TEST_SORT_KEY, ascendingKey); + producer.send(msg); + _producerSession.commit(); + } + + synchronized(consumerThread) + { + try + { + consumerThread.join(5000L); + } + catch(InterruptedException e) + { + fail("Test failed waiting for consumer to complete"); + } + } + assertTrue("Consumer timed out", consumerThread.isStopped()); + assertEquals("Incorrect number of messages received", 200, consumerThread.getConsumed()); + + producer.close(); + } + + public void testSortOrderWithNonUniqueKeys() throws JMSException, NamingException, AMQException + { + final Queue queue = createQueue(); + final MessageProducer producer = _producerSession.createProducer(queue); + + int count = 0; + while(count < 200) + { + final Message msg = _producerSession.createTextMessage("Message Text:" + count++); + msg.setStringProperty(TEST_SORT_KEY, "samesortkeyvalue"); + producer.send(msg); + } + + _producerSession.commit(); + producer.close(); + + final Session consumerSession = _consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final MessageConsumer consumer = consumerSession.createConsumer(queue); + _consumerConnection.start(); + TextMessage received = null; + int messageCount = 0; + + while((received = (TextMessage) consumer.receive(1000)) != null) + { + assertEquals("Received message with unexpected sorted key value", "samesortkeyvalue", + received.getStringProperty(TEST_SORT_KEY)); + assertEquals("Received message with unexpected message value", "Message Text:" + messageCount, + received.getText()); + messageCount++; + } + + assertEquals("Incorrect number of messages received", 200, messageCount); + } + + public void testSortOrderWithUniqueKeySubset() throws JMSException, NamingException, AMQException + { + final Queue queue = createQueue(); + final MessageProducer producer = _producerSession.createProducer(queue); + + int count = 0; + while(count < 100) + { + int keyValueIndex = count % 10; + final Message msg = _producerSession.createTextMessage("Message Text:" + count); + msg.setStringProperty(TEST_SORT_KEY, SUBSET_KEYS[keyValueIndex]); + producer.send(msg); + count++; + } + + _producerSession.commit(); + producer.close(); + + final Session consumerSession = _consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final MessageConsumer consumer = consumerSession.createConsumer(queue); + _consumerConnection.start(); + TextMessage received; + int messageCount = 0; + + while((received = (TextMessage) consumer.receive(1000)) != null) + { + assertEquals("Received message with unexpected sorted key value", SUBSET_KEYS[messageCount / 10], + received.getStringProperty(TEST_SORT_KEY)); + messageCount++; + } + + assertEquals("Incorrect number of messages received", 100, messageCount); + } + + public void testGetNextWithAck() throws JMSException, NamingException, AMQException + { + Queue _queue = createQueue(); + MessageProducer producer = _producerSession.createProducer(_queue); + Message received = null; + + //Send 3 out of order + sendAndCommitMessage(producer,"2"); + sendAndCommitMessage(producer,"3"); + sendAndCommitMessage(producer,"1"); + + final Session consumerSession = _consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + final MessageConsumer consumer = consumerSession.createConsumer(_queue); + _consumerConnection.start(); + + //Receive 3 in sorted order + received = receiveAndValidateMessage(consumer, "1"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "2"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "3"); + received.acknowledge(); + + //Send 1 + sendAndCommitMessage(producer,"4"); + + //Receive 1 and recover + received = receiveAndValidateMessage(consumer, "4"); + consumerSession.recover(); + + //Receive same 1 + received = receiveAndValidateMessage(consumer, "4"); + received.acknowledge(); + + //Send 3 out of order + sendAndCommitMessage(producer,"7"); + sendAndCommitMessage(producer,"6"); + sendAndCommitMessage(producer,"5"); + + //Receive 1 of 3 (out of order due to pre-fetch) and recover + received = receiveAndValidateMessage(consumer, "7"); + consumerSession.recover(); + + if (isBroker010()) + { + //Receive 3 in sorted order (not as per JMS recover) + received = receiveAndValidateMessage(consumer, "5"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "6"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "7"); + received.acknowledge(); + } + else + { + //Receive 3 in partial sorted order due to recover + received = receiveAndValidateMessage(consumer, "7"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "5"); + received.acknowledge(); + received = receiveAndValidateMessage(consumer, "6"); + received.acknowledge(); + } + } + + protected Queue createQueue() throws AMQException, JMSException + { + final Map<String, Object> arguments = new HashMap<String, Object>(); + arguments.put(AMQQueueFactory.QPID_QUEUE_SORT_KEY, TEST_SORT_KEY); + ((AMQSession<?,?>) _producerSession).createQueue(new AMQShortString(getTestQueueName()), false, true, false, arguments); + final Queue queue = new AMQQueue("amq.direct", getTestQueueName()); + ((AMQSession<?,?>) _producerSession).declareAndBind((AMQDestination) queue); + return queue; + } + + private Message getSortableTestMesssage(final String key) throws JMSException + { + final Message msg = _producerSession.createTextMessage("Message Text: Key Value" + key); + msg.setStringProperty(TEST_SORT_KEY, key); + return msg; + } + + private void sendAndCommitMessage(final MessageProducer producer, final String keyValue) throws JMSException + { + producer.send(getSortableTestMesssage(keyValue)); + _producerSession.commit(); + } + + private Message receiveAndValidateMessage(final MessageConsumer consumer, final String expectedKey) throws JMSException + { + final Message received = (TextMessage) consumer.receive(10000); + assertNotNull("Received message is unexpectedly null", received); + assertEquals("Received message with unexpected sorted key value", expectedKey, + received.getStringProperty(TEST_SORT_KEY)); + return received; + } + + private class TestConsumerThread extends Thread + { + private boolean _stopped = false; + private int _count = 0; + private int _consumed = 0; + private int _sessionType = Session.AUTO_ACKNOWLEDGE; + private Queue _queue; + + public TestConsumerThread(final int sessionType, final Queue queue) + { + _sessionType = sessionType; + _queue = queue; + } + + public void run() + { + try + { + Connection conn = null; + try + { + conn = getConnection(); + } + catch(Exception e) + { + fail("Could not get connection"); + } + + final Session session = conn.createSession((_sessionType == Session.SESSION_TRANSACTED ? true : false), + _sessionType); + final MessageConsumer consumer = session.createConsumer(_queue); + + conn.start(); + + TextMessage msg; + Calendar cal = Calendar.getInstance(Locale.UK); + while((msg = (TextMessage) consumer.receive(1000)) != null) + { + if(_sessionType == Session.SESSION_TRANSACTED) + { + if (_count%10 == 0) + { + LOGGER.debug("transacted session rollback"); + session.rollback(); + } + else + { + LOGGER.debug("transacted session commit"); + session.commit(); + _consumed++; + } + } + else if(_sessionType == Session.CLIENT_ACKNOWLEDGE) + { + if (_count%10 == 0) + { + LOGGER.debug("client ack session recover"); + session.recover(); + } + else + { + LOGGER.debug("client ack session acknowledge"); + msg.acknowledge(); + _consumed++; + } + } + else + { + LOGGER.debug("auto ack session"); + _consumed++; + } + + _count++; + LOGGER.debug("Message consumed at : " + cal.getTimeInMillis()); + LOGGER.debug("Message consumed with key: " + msg.getStringProperty(TEST_SORT_KEY)); + LOGGER.debug("Message consumed with text: " + msg.getText()); + LOGGER.debug("Message consumed with consumed index: " + _consumed); + } + + _stopped = true; + session.close(); + conn.close(); + } + catch(JMSException e) + { + e.printStackTrace(); + } + } + + public synchronized boolean isStopped() + { + return _stopped; + } + + public synchronized int getConsumed() + { + return _consumed; + } + } + + private static class AscendingSortedKeys + { + public static final String[] KEYS = { "Ul4a1", "WaWsv", "2Yz7E", "ix74r", "okgRi", "HlUbF", "LewvM", "lweGy", + "TXQ0Z", "0Kyfs", "s7Mxk", "dmoS7", "8RCUA", "W3VFH", "aez9y", "uQIcz", "0h1b1", "cmXIX", + "4dEz6", "zHF1q", "D6rBy", "5drc6", "0BmCy", "BCxeC", "t59lR", "aL6AJ", "OHaBz", "WmadA", + "B3qem", "CxVEf", "AIYUu", "uJScX", "uoStw", "ogLgc", "AgJHQ", "hUTw7", "Rxrsm", "9GXkX", + "7hyVv", "y94nw", "Twano", "TCgPp", "pFrrl", "POUYS", "L7cGc", "0ao3l", "CNHmv", "MaJQs", + "OUqFM", "jeskS", "FPfSE", "v1Hln", "14FLR", "KZamH", "G1RhS", "FVMxo", "rKDLJ", "nnP8o", + "nFqik", "zmLYD", "1j5L8", "e6e4z", "WDVWJ", "aDGtS", "fcwDa", "nlaBy", "JJs5m", "vLsmS", + "No0Qb", "JXljW", "Waim6", "MezSW", "l83Ud", "SjskQ", "uPX7G", "5nmWv", "ZhwG1", "uTacx", + "t98iW", "JkzUn", "fmIK1", "i7WMQ", "bgJAz", "n1pmO", "jS1aj", "4W0Tl", "Yf2Ec", "sqVrf", + "MojnP", "qQxHP", "pWiOs", "yToGW", "kB5nP", "BpYhV", "Cfgr3", "zbIYY", "VLTy6", "he9IA", + "lm0pD", "WreyP", "8hJdt", "QnJ1S", "n8pJ9", "iqv4k", "OUYuF", "8cVD3", "sx5Gl", "cQOnv", + "wiHrZ", "oGu6x", "7fsYM", "gf8rI", "7fKYU", "pT8wu", "lCMxy", "prNT6", "5Drn0", "guMb8", + "OxWIH", "uZPqg", "SbRYy", "In3NS", "uvf7A", "FLsph", "pmeCd", "BbwgA", "ru4UG", "YOfrY", + "W7cTs", "K4GS8", "AOgEe", "618Di", "dpe1v", "3otm6", "oVQp6", "5Mg9r", "Y1mC0", "VIlwP", + "aFFss", "Mkgy8", "pv0i7", "S77LH", "XyPZN", "QYxC0", "vkCHH", "MGlTF", "24ARF", "v2eC3", + "ZUnqt", "HfyNQ", "FjHXR", "45cIH", "1LB1L", "zqH0W", "fLNg8", "oQ87r", "Cp3mZ", "Zv7z0", + "O3iyQ", "EOE1o", "5ZaEz", "tlILt", "MmsIo", "lXFOB", "gtCA5", "yEfy9", "7X3uy", "d7vjM", + "XflUq", "Fhtgl", "NOHsz", "GWqqX", "xciqp", "BFkb8", "P6bcg", "lViBv", "2TRI7", "2hEEU", + "9XyT9", "29QAz", "U3yw5", "FxX9q", "C2Irc", "8U2nU", "m4bxU", "5iGN5", "mX2GE", "cShY2", + "JRJQB", "yvOMI", "4QMc9", "NAFuw", "RmDcr", "faHir", "2ZHdk", "zY1GY", "a00b5", "ZuDtD", + "JIqXi", "K20wK", "gdQsS", "5Namm", "lkMUA", "IBe8k", "FcWrW", "FFDui", "tuDyS", "ZJTXH", + "AkKTk", "zQt6Q", "FNYIM", "RpBQm", "RsQUq", "Mm8si", "gjUTu", "zz4ZU", "jiVBP", "ReKEW", + "5VZjS", "YjB9t", "zFgtB", "8TxD7", "euZA5", "MK07Y", "CK5W7", "16lHc", "6q6L9", "Z4I1v", + "UlU3M", "SWfou", "0PktI", "55rfB", "jfREu", "580YD", "Uvlv4", "KASQ8", "AmdQd", "piJSk", + "hE1Ql", "LDk6f", "NcICA", "IKxdL", "iwzGk", "uN6r3", "lsQGo", "QClRL", "iKqhr", "FGzgp", + "RkQke", "b29RJ", "CIShG", "9eoRc", "F6PT2", "LbRTH", "M3zXL", "GXdoH", "IjTwP", "RBhp0", + "yluBx", "mz8gx", "MmKGJ", "Q6Lix", "uupzk", "RACuj", "d85a9", "qaofN", "kZANm", "jtn0X", + "lpF6W", "suY4x", "rz7Ut", "wDajX", "1v5hH", "Yw2oU", "ksJby", "WMiS3", "lj07Q", "EdBKc", + "6AFT0", "0YAGH", "ThjNn", "JKWYR", "9iGoT", "UmaEv", "3weIF", "CdyBV", "pAhR1", "djsrv", + "xReec", "8FmFH", "Dz1R3", "Ta8f6", "DG4sT", "VjCZq", "jSjS3", "Pb1pa", "VNCPd", "Kr8ys", + "AXpwE", "ZzJHW", "Nxx9V", "jzUqR", "dhSuH", "DQimp", "07n1c", "HP433", "RzaZA", "cL0aE", + "Ss0Zu", "FnPFB", "7lUXZ", "9rlg9", "lH1kt", "ni2v1", "48cHL", "soy9t", "WPmlx", "2Nslm", + "hSSvQ", "9y4lw", "ulk41", "ECMvU", "DLhzM", "GrDg7", "x3LDe", "QChxs", "xXTI4", "Gv3Fq", + "rhl0J", "QssNC", "brhlQ", "s93Ml", "tl72W", "pvgjS", "Qworu", "DcpWB", "X6Pin", "J2mQi", + "BGaQY", "CqqaD", "NhXdu", "dQ586", "Yh1hF", "HRxd8", "PYBf4", "64s8N", "tvdkD", "azIWp", + "tAOsr", "v8yFN", "h1zcH", "SmGzv", "bZLvS", "fFDrJ", "Oz8yZ", "0Wr5y", "fcJOy", "7ku1p", + "QbxXc", "VerEA", "QWxoT", "hYBCK", "o8Uyd", "FwEJz", "hi5X7", "uAWyp", "I7p2a", "M6qcG", + "gIYvE", "HzZT8", "iB08l", "StlDJ", "tjQxs", "k85Ae", "taOXK", "s4786", "2DREs", "atef2", + "Vprf2", "VBjhz", "EoToP", "blLA9", "qUJMd", "ydG8U", "8xEKz", "uLtKs", "GSQwj", "S2Dfu", + "ciuWz", "i3pyd", "7Ow5C", "IRh48", "vOqCE", "Q6hMC", "yofH3", "KsjRK", "5IhmG", "fqypy", + "0MR5X", "Chuy3" }; + + private static int _i = 0; + private static int _j = 0; + + static + { + Arrays.sort(KEYS); + } + + public static String getNextKey() + { + if(_j == KEYS.length) + { + _j = 0; + _i++; + if(_i == KEYS.length) + { + _i = 0; + } + } + return new StringBuffer().append(KEYS[_i]).append("-").append(KEYS[_j++]).toString(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java deleted file mode 100644 index c41de94121..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.qpid.AMQException; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.framing.AMQShortString; - -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; - -public class SubscriptionTestHelper implements Subscription -{ - private final List<QueueEntry> messages; - private final Object key; - private boolean isSuspended; - private AMQQueue.Context _queueContext; - - public SubscriptionTestHelper(Object key) - { - this(key, new ArrayList<QueueEntry>()); - } - - public SubscriptionTestHelper(final Object key, final boolean isSuspended) - { - this(key); - setSuspended(isSuspended); - } - - SubscriptionTestHelper(Object key, List<QueueEntry> messages) - { - this.key = key; - this.messages = messages; - } - - List<QueueEntry> getMessages() - { - return messages; - } - - public void setQueue(AMQQueue queue, boolean exclusive) - { - - } - - public void setNoLocal(boolean noLocal) - { - - } - - public void send(QueueEntry msg) - { - messages.add(msg); - } - - public void setSuspended(boolean suspended) - { - isSuspended = suspended; - } - - public boolean isSuspended() - { - return isSuspended; - } - - public boolean wouldSuspend(QueueEntry msg) - { - return isSuspended; - } - - public void addToResendQueue(QueueEntry msg) - { - //no-op - } - - public void getSendLock() - { - return; - } - - public void releaseSendLock() - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void resend(final QueueEntry entry) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void onDequeue(final QueueEntry queueEntry) - { - - } - - public void restoreCredit(QueueEntry queueEntry) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void setStateListener(final StateListener listener) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public State getState() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public AMQQueue.Context getQueueContext() - { - return _queueContext; - } - - public void setQueueContext(AMQQueue.Context queueContext) - { - _queueContext = queueContext; - } - - public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue) - { - return false; //To change body of implemented methods use File | Settings | File Templates. - } - - public AMQChannel getChannel() - { - return null; - } - - public void start() - { - //no-op - } - - public AMQShortString getConsumerTag() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public long getSubscriptionID() - { - return 0; //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isActive() - { - return false; //To change body of implemented methods use File | Settings | File Templates. - } - - public void set(String key, Object value) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public Object get(String key) - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public LogActor getLogActor() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isTransient() - { - return false; - } - - public AMQQueue getQueue() - { - return null; - } - - public QueueEntry.SubscriptionAcquiredState getOwningState() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public QueueEntry.SubscriptionAssignedState getAssignedState() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public void queueDeleted(AMQQueue queue) - { - } - - public boolean filtersMessages() - { - return false; - } - - public boolean hasInterest(QueueEntry msg) - { - return true; - } - - public Queue<QueueEntry> getPreDeliveryQueue() - { - return null; - } - - public Queue<QueueEntry> getResendQueue() - { - return null; - } - - public Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages) - { - return messages; - } - - public void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst) - { - //no-op - } - - public void close() - { - //no-op - } - - public boolean isClosed() - { - return false; - } - - public boolean acquires() - { - return true; - } - - public boolean seesRequeues() - { - return true; - } - - public boolean isBrowser() - { - return false; - } - - public int hashCode() - { - return key.hashCode(); - } - - public boolean equals(Object o) - { - return o instanceof SubscriptionTestHelper && ((SubscriptionTestHelper) o).key.equals(key); - } - - public String toString() - { - return key.toString(); - } - - public boolean isSessionTransactional() - { - return false; - } - - public void queueEmpty() throws AMQException - { - //TODO - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java index 32b0185f88..262051ff89 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java @@ -24,8 +24,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -53,31 +51,25 @@ import org.apache.qpid.url.URLSyntaxException; * TODO move the pre broker-startup setup method invocation code to {@link QpidBrokerTestCase} * * @see ExternalACLTest - * @see ExternalACLFileTest * @see ExternalACLJMXTest - * @see ExternalAdminACLTest * @see ExhaustiveACLTest */ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements ConnectionListener { /** Used to synchronise {@link #tearDown()} when exceptions are thrown */ - protected CountDownLatch _exceptionReceived; - - /** Override this to return the name of the configuration XML file. */ - public abstract String getConfig(); + protected CountDownLatch _exceptionReceived; - /** Override this to setup external ACL files for virtual hosts. */ - public List<String> getHostList() + /** Override this to return the name of the configuration XML file. */ + public String getConfig() { - return Collections.emptyList(); + return "config-systests.xml"; } - + /** * This setup method checks {@link #getConfig()} and {@link #getHostList()} to initialise the broker with specific * ACL configurations and then runs an optional per-test setup method, which is simply a method with the same name * as the test, but starting with {@code setUp} rather than {@code test}. * - * @see #setUpACLFile(String) * @see org.apache.qpid.test.utils.QpidBrokerTestCase#setUp() */ @Override @@ -85,12 +77,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements { // Initialise ACLs. _configFile = new File("build" + File.separator + "etc" + File.separator + getConfig()); - // Initialise ACL files - for (String virtualHost : getHostList()) - { - setUpACLFile(virtualHost); - } - + // run test specific setup String testSetup = StringUtils.replace(getName(), "test", "setUp"); try @@ -124,73 +111,27 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements } } - /** - * Configures specific ACL files for a virtual host. - * - * This method checks for ACL files that exist on the filesystem. If dynamically generatyed ACL files are required in a test, - * then it is easier to use the {@code setUp} prefix on a method to generate the ACL file. In order, this method looks - * for three files: - * <ol> - * <li><em>virtualhost</em>-<em>class</em>-<em>test</em>.txt - * <li><em>virtualhost</em>-<em>class</em>.txt - * <li><em>virtualhost</em>-default.txt - * </ol> - * The <em>class</em> and <em>test</em> parts are the test class and method names respectively, with the word {@code test} - * removed and the rest of the text converted to lowercase. For example, the test class and method named - * {@code org.apache.qpid.test.AccessExampleTest#testExampleMethod} on the {@code testhost} virtualhost would use - * one of the following files: - * <ol> - * <li>testhost-accessexample-examplemethod.txt - * <li>testhost-accessexample.txt - * <li>testhost-default.txt - * </ol> - * These files should be copied to the <em>${QPID_HOME}/etc</em> directory when the test is run. - * - * @see #writeACLFile(String, String...) - */ - public void setUpACLFile(String virtualHost) throws IOException, ConfigurationException + public void writeACLFile(final String vhost, final String...rules) throws ConfigurationException, IOException { - String path = "build" + File.separator + "etc"; - String className = StringUtils.substringBeforeLast(getClass().getSimpleName().toLowerCase(), "test"); - String testName = StringUtils.substringAfter(getName(), "test").toLowerCase(); - - File aclFile = new File(path, virtualHost + "-" + className + "-" + testName + ".txt"); - if (!aclFile.exists()) - { - aclFile = new File(path, virtualHost + "-" + className + ".txt"); - if (!aclFile.exists()) - { - aclFile = new File(path, virtualHost + "-" + "default.txt"); - } - } - - // Set the ACL file configuration property - if (virtualHost.equals("global")) - { - setConfigurationProperty("security.aclv2", aclFile.getAbsolutePath()); - } - else - { - setConfigurationProperty("virtualhosts.virtualhost." + virtualHost + ".security.aclv2", aclFile.getAbsolutePath()); - } + writeACLFileUtil(this, vhost, rules); } - public void writeACLFile(String vhost, String...rules) throws ConfigurationException, IOException + public static void writeACLFileUtil(QpidBrokerTestCase testcase, String vhost, String...rules) throws ConfigurationException, IOException { - File aclFile = File.createTempFile(getClass().getSimpleName(), getName()); + File aclFile = File.createTempFile(testcase.getClass().getSimpleName(), testcase.getName()); aclFile.deleteOnExit(); - if ("global".equals(vhost)) + if (vhost == null) { - setConfigurationProperty("security.aclv2", aclFile.getAbsolutePath()); + testcase.setConfigurationProperty("security.acl", aclFile.getAbsolutePath()); } else { - setConfigurationProperty("virtualhosts.virtualhost." + vhost + ".security.aclv2", aclFile.getAbsolutePath()); + testcase.setConfigurationProperty("virtualhosts.virtualhost." + vhost + ".security.acl", aclFile.getAbsolutePath()); } PrintWriter out = new PrintWriter(new FileWriter(aclFile)); - out.println(String.format("# %s", getTestName())); + out.println(String.format("# %s", testcase.getName())); for (String line : rules) { out.println(line); @@ -265,7 +206,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements public void check403Exception(Throwable t) throws Exception { assertNotNull("There was no linked exception", t); - assertTrue("Wrong linked exception type", t instanceof AMQException); + assertTrue("Wrong linked exception type : " + t.getClass(), t instanceof AMQException); assertEquals("Incorrect error code received", 403, ((AMQException) t).getErrorCode().getCode()); //use the latch to ensure the control thread waits long enough for the exception thread diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java index 1b2c98d30a..4f2464e186 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java @@ -18,9 +18,6 @@ */ package org.apache.qpid.server.security.acl; -import java.util.Arrays; -import java.util.List; - import javax.jms.Connection; import javax.jms.Session; @@ -31,7 +28,7 @@ import org.apache.qpid.protocol.AMQConstant; /** * ACL version 2/3 file testing to verify that ACL entries control queue creation with specific properties. - * + * * Tests have their own ACL files that setup specific permissions, and then try to create queues with every possible combination * of properties to show that rule matching works correctly. For example, a rule that specified {@code autodelete="true"} for * queues with {@link name="temp.true.*"} as well should not affect queues that have names that do not match, or queues that @@ -39,18 +36,7 @@ import org.apache.qpid.protocol.AMQConstant; */ public class ExhaustiveACLTest extends AbstractACLTestCase { - @Override - public String getConfig() - { - return "config-systests-aclv2.xml"; - } - @Override - public List<String> getHostList() - { - return Arrays.asList("test", "test2"); - } - /** * Creates a queue. * @@ -130,11 +116,6 @@ public class ExhaustiveACLTest extends AbstractACLTestCase createQueueFailure("test", "client", "temp.false.07", true, false); createQueueFailure("test", "server", "temp.true.08", true, false); createQueueFailure("test", "client", "temp.other.09", false, false); - createQueueSuccess("test2", "guest", "temp.true.01", false, false); - createQueueSuccess("test2", "guest", "temp.false.02", true, false); - createQueueSuccess("test2", "guest", "temp.true.03", true, false); - createQueueSuccess("test2", "guest", "temp.false.04", false, false); - createQueueSuccess("test2", "guest", "temp.other.05", false, false); } public void setUpAuthoriseCreateQueue() throws Exception @@ -161,10 +142,6 @@ public class ExhaustiveACLTest extends AbstractACLTestCase createQueueFailure("test", "server", "create.05", true, false); createQueueFailure("test", "server", "create.06", false, true); createQueueFailure("test", "server", "create.07", true, false); - createQueueSuccess("test2", "guest", "create.00", true, true); - createQueueSuccess("test2", "guest", "create.01", true, false); - createQueueSuccess("test2", "guest", "create.02", false, true); - createQueueSuccess("test2", "guest", "create.03", true, false); } public void setUpAuthoriseCreateQueueBoth() throws Exception @@ -190,6 +167,5 @@ public class ExhaustiveACLTest extends AbstractACLTestCase createQueueSuccess("test", "client", "tmp.00", true, false); createQueueSuccess("test", "server", "tmp.01", true, false); createQueueSuccess("test", "guest", "tmp.02", true, false); - createQueueSuccess("test2", "guest", "create.02", false, false); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java deleted file mode 100644 index 1d08015669..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLFileTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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.qpid.server.security.acl; - -import java.util.Arrays; -import java.util.List; - -import javax.jms.Connection; -import javax.jms.Session; - -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.AMQShortString; - -/** - * Tests that ACL version 2/3 files following the specification work correctly. - * - * ACL lines that are identical in meaning apart from differences allowed by the specification, such as whitespace or case - * of tokens are set up for numbered queues and the queues are then created to show that the meaning is correctly parsed by - * the plugin. - * - * TODO move this to the access-control plugin unit tests instead - */ -public class ExternalACLFileTest extends AbstractACLTestCase -{ - @Override - public String getConfig() - { - return "config-systests-aclv2.xml"; - } - - @Override - public List<String> getHostList() - { - return Arrays.asList("test"); - } - - private void createQueuePrefixList(String prefix, int count) - { - try - { - Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - conn.start(); - - //Create n queues - for (int n = 0; n < count; n++) - { - AMQShortString queueName = new AMQShortString(String.format("%s.%03d", prefix, n)); - ((AMQSession<?, ?>) sess).createQueue(queueName, false, false, false); - } - - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } - } - - private void createQueueNameList(String...queueNames) - { - try - { - Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - conn.start(); - - //Create all queues - for (String queueName : queueNames) - { - ((AMQSession<?, ?>) sess).createQueue(new AMQShortString(queueName), false, false, false); - } - - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } - } - - public void setUpCreateQueueMixedCase() throws Exception - { - writeACLFile( - "test", - "acl allow client create queue name=mixed.000", - "ACL ALLOW client CREATE QUEUE NAME=mixed.001", - "Acl Allow client Create Queue Name=mixed.002", - "aCL aLLOW client cREATE qUEUE nAME=mixed.003", - "aCl AlLoW client cReAtE qUeUe NaMe=mixed.004" - ); - } - - public void testCreateQueueMixedCase() - { - createQueuePrefixList("mixed", 5); - } - - public void setUpCreateQueueContinuation() throws Exception - { - writeACLFile( - "test", - "acl allow client create queue name=continuation.000", - "acl allow client create queue \\", - " name=continuation.001", - "acl allow client \\", - " create queue \\", - " name=continuation.002", - "acl allow \\", - " client \\", - " create queue \\", - " name=continuation.003", - "acl \\", - " allow \\", - " client \\", - " create queue \\", - " name=continuation.004" - ); - } - - public void testCreateQueueContinuation() - { - createQueuePrefixList("continuation", 5); - } - - public void setUpCreateQueueWhitespace() throws Exception - { - writeACLFile( - "test", - "acl allow client create queue name=whitespace.000", - "acl\tallow\tclient\tcreate\tqueue\tname=whitespace.001", - "acl allow client create queue name = whitespace.002", - "acl\tallow\tclient\tcreate\tqueue\tname\t=\twhitespace.003", - "acl allow\t\tclient\t \tcreate\t\t queue\t \t name \t =\t \twhitespace.004" - ); - } - - public void testCreateQueueWhitespace() - { - createQueuePrefixList("whitespace", 5); - } - - public void setUpCreateQueueQuoting() throws Exception - { - writeACLFile( - "test", - "acl allow client create queue name='quoting.ABC.000'", - "acl allow client create queue name='quoting.*.000'", - "acl allow client create queue name='quoting.#.000'", - "acl allow client create queue name='quoting. .000'", - "acl allow client create queue name='quoting.!@$%.000'" - ); - } - - public void testCreateQueueQuoting() - { - createQueueNameList( - "quoting.ABC.000", - "quoting.*.000", - "quoting.#.000", - "quoting. .000", - "quoting.!@$%.000" - ); - } -} - - - diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java index b823690002..427d253ca5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java @@ -18,41 +18,31 @@ */ package org.apache.qpid.server.security.acl; -import java.util.Arrays; -import java.util.List; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; -import org.apache.qpid.AMQConnectionClosedException; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQSecurityException; -import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.management.common.mbeans.ServerInformation; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.test.utils.JMXTestUtils; /** - * Tests that ACL entries that apply to AMQP objects also apply when those objects are accessed via JMX. + * Tests that access to the JMX interface is governed only by {@link ObjectType#METHOD}/{@link ObjectType#ALL} + * rules and AMQP rights have no effect. + * + * Ensures that objects outside the Qpid domain ({@link ManagedObject#DOMAIN}) are not governed by the ACL model. */ public class ExternalACLJMXTest extends AbstractACLTestCase { private JMXTestUtils _jmx; - - private static final String QUEUE_NAME = "kipper"; - private static final String EXCHANGE_NAME = "amq.kipper"; - - @Override - public String getConfig() - { - return "config-systests-aclv2.xml"; - } - @Override - public List<String> getHostList() - { - return Arrays.asList("test"); - } + private static final String TEST_QUEUE_OWNER = "admin"; + private static final String TEST_VHOST = "test"; @Override public void setUp() throws Exception { - _jmx = new JMXTestUtils(this, "admin", "admin"); + _jmx = new JMXTestUtils(this); _jmx.setUp(); super.setUp(); _jmx.open(); @@ -65,180 +55,264 @@ public class ExternalACLJMXTest extends AbstractACLTestCase super.tearDown(); } - // test-externalacljmx.txt - // create queue owner=client # success - public void testCreateClientQueueSuccess() throws Exception - { - //Queue Parameters - String queueOwner = "client"; - - _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); + /** + * Ensure an empty ACL defaults to DENY ALL. + */ + public void setUpDenyAllIsDefault() throws Exception + { + writeACLFile(null, "#Empty ACL file"); } - // test-externalacljmx.txt - // create queue owner=client # failure - public void testCreateServerQueueFailure() throws Exception - { - //Queue Parameters - String queueOwner = "server"; - + public void testDenyAllIsDefault() throws Exception + { + //try a broker-level method + ServerInformation info = _jmx.getServerInformation(); try { - _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); - - fail("Queue create should fail"); + info.resetStatistics(); + fail("Exception not thrown"); } - catch (Exception e) + catch (SecurityException e) { - assertNotNull("Cause is not set", e.getCause()); - assertEquals("Cause message incorrect", - "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Update resetStatistics", e.getMessage()); } - } - // no create queue acl in file # failure - public void testCreateQueueFailure() throws Exception - { - //Queue Parameters - String queueOwner = "guest"; - + //try a vhost-level method try { - _jmx.createQueue("test", QUEUE_NAME, queueOwner, true); - - fail("Queue create should fail"); + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + fail("Exception not thrown"); } catch (Exception e) { - assertNotNull("Cause is not set", e.getCause()); - assertEquals("Cause message incorrect", - "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Update createNewQueue", e.getMessage()); } + + // Ensure that calls to MBeans outside the Qpid domain are not impeded. + final RuntimeMXBean runtimeBean = _jmx.getManagedObject(RuntimeMXBean.class, ManagementFactory.RUNTIME_MXBEAN_NAME); + runtimeBean.getName(); + // PASS } - // test-externalacljmx.txt - // allow create exchange name=amq.kipper.success - public void testCreateExchangeSuccess() throws Exception - { - _jmx.createExchange("test", EXCHANGE_NAME + ".success", "direct", true); + /** + * Ensure an ALLOW ALL ALL rule allows access to both getters/setters. + */ + public void setUpAllowAll() throws Exception + { + writeACLFile(null, "ACL ALLOW ALL ALL"); } - // test-externalacljmx.txt - // deny create exchange name=amq.kipper.failure - public void testCreateExchangeFailure() throws Exception - { - try - { - _jmx.createExchange("test", EXCHANGE_NAME + ".failure", "direct", true); - - fail("Exchange create should fail"); - } - catch (Exception e) - { - assertNotNull("Cause is not set", e.getCause()); - assertEquals("Cause message incorrect", - "org.apache.qpid.AMQSecurityException: Permission denied: exchange-name 'amq.kipper.failure' [error code 403: access refused]", e.getCause().getMessage()); - } + public void testAllowAll() throws Exception + { + ServerInformation info = _jmx.getServerInformation(); + info.getBuildVersion(); // getter - requires ACCESS + info.resetStatistics(); // setter - requires UPDATE + // PASS } - // test-externalacljmx.txt - // allow create exchange name=amq.kipper.success - // allow delete exchange name=amq.kipper.success - public void testDeleteExchangeSuccess() throws Exception - { - _jmx.createExchange("test", EXCHANGE_NAME + ".success", "direct", true); - _jmx.unregisterExchange("test", EXCHANGE_NAME + ".success"); + /** + * admin user is denied at broker level but allowed at vhost level. + */ + public void setUpVhostAllowOverridesGlobalDeny() throws Exception + { + writeACLFile(null, + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile(TEST_VHOST, + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); } - // test-externalacljmx-deleteexchangefailure.txt - // allow create exchange name=amq.kipper.delete - // deny delete exchange name=amq.kipper.delete - public void testDeleteExchangeFailure() throws Exception - { - _jmx.createExchange("test", EXCHANGE_NAME + ".delete", "direct", true); + public void testVhostAllowOverridesGlobalDeny() throws Exception + { + //try a vhost-level method on the allowed vhost + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + + //try a vhost-level method on a different vhost try { - _jmx.unregisterExchange("test", EXCHANGE_NAME + ".delete"); - - fail("Exchange delete should fail"); + _jmx.createQueue("development", getTestQueueName(), TEST_QUEUE_OWNER, true); + fail("Exception not thrown"); } - catch (Exception e) + catch (SecurityException e) { - assertNotNull("Cause is not set", e.getCause()); - assertEquals("Cause message incorrect", - "org.apache.qpid.AMQSecurityException: Permission denied [error code 403: access refused]", e.getCause().getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Update createNewQueue", e.getMessage()); } } - + + /** - * admin user has JMX right but not AMQP + * admin user is allowed all update methods on the component at broker level. */ - public void setUpCreateQueueJMXRights() throws Exception + public void setUpUpdateComponentOnlyAllow() throws Exception { - writeACLFile("test", - "ACL ALLOW admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", - "ACL DENY admin CREATE QUEUE"); + writeACLFile(null, + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager'"); } - - public void testCreateQueueJMXRights() throws Exception + + public void testUpdateComponentOnlyAllow() throws Exception + { + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + // PASS + _jmx.deleteQueue(TEST_VHOST, getTestQueueName()); + // PASS + } + + + /** + * admin user is allowed all update methods on all components at broker level. + */ + public void setUpUpdateMethodOnlyAllow() throws Exception + { + writeACLFile(null, + "ACL ALLOW admin UPDATE METHOD"); + } + + public void testUpdateMethodOnlyAllow() throws Exception + { + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + //PASS + _jmx.deleteQueue(TEST_VHOST, getTestQueueName()); + // PASS + } + + + /** + * admin user has JMX right, AMPQ right is irrelevant. + */ + public void setUpCreateQueueSuccess() throws Exception + { + writeACLFile(TEST_VHOST, + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + } + + public void testCreateQueueSuccess() throws Exception + { + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + } + + + /** + * admin user has JMX right, verifies lack of AMPQ rights is irrelevant. + */ + public void setUpCreateQueueSuccessNoAMQPRights() throws Exception + { + writeACLFile(TEST_VHOST, + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'", + "ACL DENY admin CREATE QUEUE"); + } + + public void testCreateQueueSuccessNoAMQPRights() throws Exception + { + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + } + + + /** + * admin user does not have JMX right, AMPQ right is irrelevant. + */ + public void setUpCreateQueueDenied() throws Exception + { + writeACLFile(TEST_VHOST, + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + } + + public void testCreateQueueDenied() throws Exception { try { - _jmx.createQueue("test", QUEUE_NAME, "admin", true); - - fail("Queue create should fail"); + _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); + fail("Exception not thrown"); } - catch (Exception e) + catch (SecurityException e) { - assertNotNull("Cause is not set", e.getCause()); - assertEquals("Cause message incorrect", - "org.apache.qpid.AMQSecurityException: Permission denied: queue-name 'kipper' [error code 403: access refused]", e.getCause().getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Update createNewQueue", e.getMessage()); } } + /** - * admin user has AMQP right but not JMX + * admin user does not have JMX right */ - public void setUpCreateQueueAMQPRights() throws Exception + public void setUpServerInformationUpdateDenied() throws Exception { - writeACLFile("test", - "ACL DENY admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", - "ACL ALLOW admin CREATE QUEUE"); + writeACLFile(null, + "ACL DENY admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); } - - public void testCreateQueueAMQPRights() throws Exception + + public void testServerInformationUpdateDenied() throws Exception { + ServerInformation info = _jmx.getServerInformation(); try { - _jmx.createQueue("test", QUEUE_NAME, "admin", true); - - fail("Queue create should fail"); + info.resetStatistics(); + fail("Exception not thrown"); } - catch (Exception e) + catch (SecurityException e) { - assertEquals("Cause message incorrect", "Permission denied: Execute createNewQueue", e.getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Update resetStatistics", e.getMessage()); } } + /** - * admin has both JMX and AMQP rights + * admin user has JMX right to check management API major version (but not minor version) */ - public void setUpCreateQueueJMXAMQPRights() throws Exception + public void setUpServerInformationAccessGranted() throws Exception { - writeACLFile("test", - "ACL ALLOW admin EXECUTE METHOD component=\"VirtualHost.VirtualHostManager\" name=\"createNewQueue\"", - "ACL ALLOW admin CREATE QUEUE"); + writeACLFile(null, + "ACL ALLOW-LOG admin ACCESS METHOD component='ServerInformation' name='getManagementApiMajorVersion'"); } - - public void testCreateQueueJMXAMQPRights() throws Exception + + public void testServerInformationAccessGranted() throws Exception { + ServerInformation info = _jmx.getServerInformation(); + info.getManagementApiMajorVersion(); + try { - _jmx.createQueue("test", QUEUE_NAME, "admin", true); + info.getManagementApiMinorVersion(); + fail("Exception not thrown"); } - catch (Exception e) + catch (SecurityException e) { - fail("Queue create should succeed: " + e.getCause().getMessage()); + assertEquals("Cause message incorrect", "Permission denied: Access getManagementApiMinorVersion", e.getMessage()); } } + + + /** + * admin user has JMX right to use the update method + */ + public void setUpServerInformationUpdateMethodPermission() throws Exception + { + writeACLFile(null, + "ACL ALLOW admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); + } + + public void testServerInformationUpdateMethodPermission() throws Exception + { + ServerInformation info = _jmx.getServerInformation(); + info.resetStatistics(); + // PASS + } + + + /** + * admin user has JMX right to use all types of method on ServerInformation + */ + public void setUpServerInformationAllMethodPermissions() throws Exception + { + writeACLFile(null, "ACL ALLOW admin ALL METHOD component='ServerInformation'"); + } + + public void testServerInformationAllMethodPermissions() throws Exception + { + //try an update method + ServerInformation info = _jmx.getServerInformation(); + info.resetStatistics(); + // PASS + //try an access method + info.getManagementApiMinorVersion(); + // PASS + } + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java index 254e1fe6ac..e9b8a2efd5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java @@ -18,13 +18,8 @@ */ package org.apache.qpid.server.security.acl; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - import javax.jms.Connection; -import javax.jms.DeliveryMode; -import javax.jms.IllegalStateException; +import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; @@ -37,9 +32,6 @@ import javax.jms.TopicSubscriber; import javax.naming.NamingException; import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.url.URLSyntaxException; @@ -49,7 +41,13 @@ import org.apache.qpid.url.URLSyntaxException; */ public class ExternalACLTest extends AbstractACLTestCase { - public void testAccessAuthorizedSuccess() throws AMQException, URLSyntaxException, Exception + + public void setUpAccessAuthorizedSuccess() throws Exception + { + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST"); + } + + public void testAccessAuthorizedSuccess() throws Exception { try { @@ -68,43 +66,9 @@ public class ExternalACLTest extends AbstractACLTestCase } } - public void testAccessVhostAuthorisedGuestSuccess() throws IOException, Exception + public void setUpAccessNoRightsFailure() throws Exception { - //The 'guest' user has no access to the 'test' vhost, as tested below in testAccessNoRights(), and so - //is unable to perform actions such as connecting (and by extension, creating a queue, and consuming - //from a queue etc). In order to test the vhost-wide 'access' ACL right, the 'guest' user has been given - //this right in the 'test2' vhost. - - try - { - //get a connection to the 'test2' vhost using the guest user and perform various actions. - Connection conn = getConnection("test2", "guest", "guest"); - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - conn.start(); - - //create Queues and consumers for each - Queue namedQueue = sess.createQueue("vhostAccessCreatedQueue" + getTestQueueName()); - Queue tempQueue = sess.createTemporaryQueue(); - MessageConsumer consumer = sess.createConsumer(namedQueue); - MessageConsumer tempConsumer = sess.createConsumer(tempQueue); - - //send a message to each queue (also causing an exchange declare) - MessageProducer sender = ((AMQSession<?, ?>) sess).createProducer(null); - ((org.apache.qpid.jms.MessageProducer) sender).send(namedQueue, sess.createTextMessage("test"), - DeliveryMode.NON_PERSISTENT, 0, 0L, false, false); - ((org.apache.qpid.jms.MessageProducer) sender).send(tempQueue, sess.createTextMessage("test"), - DeliveryMode.NON_PERSISTENT, 0, 0L, false, false); - - //consume the messages from the queues - consumer.receive(2000); - tempConsumer.receive(2000); - - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } + writeACLFile("test", "ACL DENY-LOG client ACCESS VIRTUALHOST"); } public void testAccessNoRightsFailure() throws Exception @@ -131,228 +95,124 @@ public class ExternalACLTest extends AbstractACLTestCase } } - public void testClientDeleteQueueSuccess() throws Exception + public void setUpClientDeleteQueueSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); - - // create kipper - Topic kipper = sess.createTopic("kipper"); - TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); - - subscriber.close(); - sess.unsubscribe("kipper"); - - //Do something to show connection is active. - sess.rollback(); - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"" , + "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", + "ACL ALLOW-LOG client DELETE QUEUE durable=\"true\"", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); } - public void testServerDeleteQueueFailure() throws Exception + public void testClientDeleteQueueSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); - // create kipper - Topic kipper = sess.createTopic("kipper"); - TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); + // create kipper + Topic kipper = sess.createTopic("kipper"); + TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); - subscriber.close(); - sess.unsubscribe("kipper"); + subscriber.close(); + sess.unsubscribe("kipper"); - //Do something to show connection is active. - sess.rollback(); - conn.close(); - } - catch (JMSException e) - { - // JMSException -> linedException = AMQException.403 - check403Exception(e.getLinkedException()); - } + //Do something to show connection is active. + sess.rollback(); + conn.close(); } - public void testClientConsumeFromTempQueueSuccess() throws AMQException, URLSyntaxException, Exception - { - try - { - Connection conn = getConnection("test", "client", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - sess.createConsumer(sess.createTemporaryQueue()); - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } + public void setUpClientDeleteQueueFailure() throws Exception + { + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"" , + "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", + "ACL DENY-LOG client DELETE QUEUE durable=\"true\"", + "ACL DENY-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); } - public void testClientConsumeFromNamedQueueFailure() throws NamingException, Exception + public void testClientDeleteQueueFailure() throws Exception { - try - { - Connection conn = getConnection("test", "client", "guest"); + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + // create kipper + Topic kipper = sess.createTopic("kipper"); + TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); - conn.start(); + subscriber.close(); + try + { + sess.unsubscribe("kipper"); - sess.createConsumer(sess.createQueue("IllegalQueue")); + //Do something to show connection is active. + sess.rollback(); - fail("Test failed as consumer was created."); + fail("Exception was not thrown"); } catch (JMSException e) { + // JMSException -> linedException = AMQException.403 check403Exception(e.getLinkedException()); } } - public void testClientCreateTemporaryQueueSuccess() throws JMSException, URLSyntaxException, Exception - { - try - { - Connection conn = getConnection("test", "client", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - //Create Temporary Queue - can't use the createTempQueue as QueueName is null. - ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("doesnt_matter_as_autodelete_means_tmp"), - true, false, false); - - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } - } - - public void testClientCreateNamedQueueFailure() throws NamingException, JMSException, AMQException, Exception + public void testClientConsumeFromTempQueueSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "client", "guest"); + Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - conn.start(); + conn.start(); - //Create a Named Queue - ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("IllegalQueue"), false, false, false); - - fail("Test failed as Queue creation succeded."); - //conn will be automatically closed - } - catch (AMQException e) - { - check403Exception(e); - } + sess.createConsumer(sess.createTemporaryQueue()); } - public void testClientPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, Exception + public void setUpClientConsumeFromNamedQueueValid() throws Exception { - try - { - Connection conn = getConnection("test", "client", "guest"); + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE name=\"example.RequestQueue\"" , + "ACL ALLOW-LOG client CONSUME QUEUE name=\"example.RequestQueue\"", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); + } - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); - - MessageProducer sender = sess.createProducer(sess.createQueue("example.RequestQueue")); + public void testClientConsumeFromNamedQueueValid() throws Exception + { + Connection conn = getConnection("test", "client", "guest"); - sender.send(sess.createTextMessage("test")); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. - sess.commit(); + conn.start(); - conn.close(); - } - catch (Exception e) - { - fail("Test publish failed:" + e); - } + sess.createConsumer(sess.createQueue("example.RequestQueue")); } - public void testClientPublishValidQueueSuccess() throws AMQException, URLSyntaxException, Exception + public void setUpClientConsumeFromNamedQueueFailure() throws Exception { - try - { - Connection conn = getConnection("test", "client", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - MessageProducer sender = ((AMQSession<?, ?>) sess).createProducer(null); - - Queue queue = sess.createQueue("example.RequestQueue"); - - // Send a message that we will wait to be sent, this should give the broker time to process the msg - // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not - // queue existence. - ((org.apache.qpid.jms.MessageProducer) sender).send(queue, sess.createTextMessage("test"), - DeliveryMode.NON_PERSISTENT, 0, 0L, false, false); - - conn.close(); - } - catch (Exception e) - { - fail("Test publish failed:" + e); - } + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE" , + "ACL ALLOW-LOG client BIND EXCHANGE", + "ACL DENY-LOG client CONSUME QUEUE name=\"IllegalQueue\""); } - public void testClientPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception + public void testClientConsumeFromNamedQueueFailure() throws NamingException, Exception { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + Destination dest = sess.createQueue("IllegalQueue"); + try { - Connection conn = getConnection("test", "client", "guest"); - - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - MessageProducer sender = ((AMQSession<?, ?>) session).createProducer(null); - - Queue queue = session.createQueue("Invalid"); - - // Send a message that we will wait to be sent, this should give the broker time to close the connection - // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not - // queue existence. - ((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"), - DeliveryMode.NON_PERSISTENT, 0, 0L, false, false); - - // Test the connection with a valid consumer - // This may fail as the session may be closed before the queue or the consumer created. - Queue temp = session.createTemporaryQueue(); + sess.createConsumer(dest); - session.createConsumer(temp).close(); - - //Connection should now be closed and will throw the exception caused by the above send - conn.close(); - - fail("Close is not expected to succeed."); - } - catch (IllegalStateException e) - { - _logger.info("QPID-2345: Session became closed and we got that error rather than the authentication error."); + fail("Test failed as consumer was created."); } catch (JMSException e) { @@ -360,39 +220,43 @@ public class ExternalACLTest extends AbstractACLTestCase } } - public void testServerConsumeFromNamedQueueValid() throws AMQException, URLSyntaxException, Exception + public void setUpClientCreateTemporaryQueueSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE temporary=\"true\"" , + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true", + "ACL ALLOW-LOG client DELETE QUEUE temporary=\"true\"", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true"); + } - conn.start(); + public void testClientCreateTemporaryQueueSuccess() throws JMSException, URLSyntaxException, Exception + { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); - sess.createConsumer(sess.createQueue("example.RequestQueue")); + sess.createTemporaryQueue(); + conn.close(); + } - conn.close(); - } - catch (Exception e) - { - fail("Test failed due to:" + e.getMessage()); - } + public void setUpClientCreateTemporaryQueueFailed() throws Exception + { + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL DENY-LOG client CREATE QUEUE temporary=\"true\""); } - public void testServerConsumeFromNamedQueueInvalid() throws AMQException, URLSyntaxException, NamingException, Exception + public void testClientCreateTemporaryQueueFailed() throws NamingException, Exception { + Connection conn = getConnection("test", "client", "guest"); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + try { - Connection conn = getConnection("test", "client", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - conn.start(); - - sess.createConsumer(sess.createQueue("Invalid")); + session.createTemporaryQueue(); - fail("Test failed as consumer was created."); + fail("Test failed as creation succeded."); } catch (JMSException e) { @@ -400,247 +264,139 @@ public class ExternalACLTest extends AbstractACLTestCase } } - public void testServerConsumeFromTemporaryQueue() throws AMQException, URLSyntaxException, NamingException, Exception + public void setUpClientCreateNamedQueueFailure() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - sess.createConsumer(sess.createTemporaryQueue()); - - fail("Test failed as consumer was created."); - } - catch (JMSException e) - { - check403Exception(e.getLinkedException()); - } + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE name=\"ValidQueue\"", + "ACL ALLOW-LOG client CONSUME QUEUE"); } - public void testServerCreateNamedQueueValid() throws JMSException, URLSyntaxException, Exception + public void testClientCreateNamedQueueFailure() throws NamingException, JMSException, AMQException, Exception { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + conn.start(); + Destination dest = sess.createQueue("IllegalQueue"); + try { - Connection conn = getConnection("test", "server", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - //Create Temporary Queue - ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("example.RequestQueue"), false, false, false); - - conn.close(); + //Create a Named Queue as side effect + sess.createConsumer(dest); + fail("Test failed as Queue creation succeded."); } - catch (Exception e) + catch (JMSException e) { - fail("Test failed due to:" + e.getMessage()); + check403Exception(e.getLinkedException()); } } - public void testServerCreateNamedQueueInvalid() throws JMSException, URLSyntaxException, AMQException, NamingException, Exception + public void setUpClientPublishUsingTransactionSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - - Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - //Create a Named Queue - ((AMQSession<?, ?>) sess).createQueue(new AMQShortString("IllegalQueue"), false, false, false); - - fail("Test failed as creation succeded."); - } - catch (Exception e) - { - check403Exception(e); - } + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE" , + "ACL ALLOW-LOG client BIND EXCHANGE", + "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); } - public void testServerCreateTemporaryQueueInvalid() throws NamingException, Exception + public void testClientPublishUsingTransactionSuccess() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Connection conn = getConnection("test", "client", "guest"); - conn.start(); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - session.createTemporaryQueue(); + conn.start(); - fail("Test failed as creation succeded."); - } - catch (JMSException e) - { - check403Exception(e.getLinkedException()); - } - } + MessageProducer sender = sess.createProducer(sess.createQueue("example.RequestQueue")); - public void testServerCreateAutoDeleteQueueInvalid() throws NamingException, JMSException, AMQException, Exception - { - try - { - Connection connection = getConnection("test", "server", "guest"); + sender.send(sess.createTextMessage("test")); - Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. + sess.commit(); - connection.start(); + conn.close(); + } - ((AMQSession<?, ?>) session).createQueue(new AMQShortString("again_ensure_auto_delete_queue_for_temporary"), - true, false, false); - fail("Test failed as creation succeded."); - } - catch (Exception e) - { - check403Exception(e); - } + public void setUpRequestResponseSuccess() throws Exception + { + writeACLFile("test", "GROUP messaging-users client server", + "ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST", + "# Server side", + "ACL ALLOW-LOG server CREATE QUEUE name=\"example.RequestQueue\"" , + "ACL ALLOW-LOG server BIND EXCHANGE", + "ACL ALLOW-LOG server PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"TempQueue*\"", + "ACL ALLOW-LOG server CONSUME QUEUE name=\"example.RequestQueue\"", + "# Client side", + "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\"", + "ACL ALLOW-LOG client CONSUME QUEUE temporary=true", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true", + "ACL ALLOW-LOG client CREATE QUEUE temporary=true", + "ACL ALLOW-LOG client DELETE QUEUE temporary=true"); } - /** - * This test uses both the cilent and sender to validate that the Server is able to publish to a temporary queue. - * The reason the client must be involved is that the Server is unable to create its own Temporary Queues. - * - * @throws AMQException - * @throws URLSyntaxException - * @throws JMSException - */ - public void testServerPublishUsingTransactionSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception + + public void testRequestResponseSuccess() throws Exception { //Set up the Server Connection serverConnection = getConnection("test", "server", "guest"); - Session serverSession = serverConnection.createSession(true, Session.SESSION_TRANSACTED); - Queue requestQueue = serverSession.createQueue("example.RequestQueue"); - MessageConsumer server = serverSession.createConsumer(requestQueue); - serverConnection.start(); //Set up the consumer Connection clientConnection = getConnection("test", "client", "guest"); - - //Send a test mesage - Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); - + Session clientSession = clientConnection.createSession(true, Session.SESSION_TRANSACTED); Queue responseQueue = clientSession.createTemporaryQueue(); - MessageConsumer clientResponse = clientSession.createConsumer(responseQueue); - clientConnection.start(); + // Client Message request = clientSession.createTextMessage("Request"); - - assertNotNull("Response Queue is null", responseQueue); - request.setJMSReplyTo(responseQueue); clientSession.createProducer(requestQueue).send(request); - - try - { - Message msg = null; - - msg = server.receive(2000); - - while (msg != null && !((TextMessage) msg).getText().equals("Request")) - { - msg = server.receive(2000); - } - - assertNotNull("Message not received", msg); - - assertNotNull("Reply-To is Null", msg.getJMSReplyTo()); - - MessageProducer sender = serverSession.createProducer(msg.getJMSReplyTo()); - - sender.send(serverSession.createTextMessage("Response")); - - //Send the message using a transaction as this will allow us to retrieve any errors that occur on the broker. - serverSession.commit(); - - //Ensure Response is received. - Message clientResponseMsg = clientResponse.receive(2000); - assertNotNull("Client did not receive response message,", clientResponseMsg); - assertEquals("Incorrect message received", "Response", ((TextMessage) clientResponseMsg).getText()); - - } - catch (Exception e) - { - fail("Test publish failed:" + e); - } - finally - { - try - { - serverConnection.close(); - } - finally - { - clientConnection.close(); - } - } + clientSession.commit(); + + // Server + Message msg = server.receive(2000); + assertNotNull("Server should have received client's request", msg); + assertNotNull("Received msg should have Reply-To", msg.getJMSReplyTo()); + + MessageProducer sender = serverSession.createProducer(msg.getJMSReplyTo()); + sender.send(serverSession.createTextMessage("Response")); + serverSession.commit(); + + // Client + Message clientResponseMsg = clientResponse.receive(2000); + clientSession.commit(); + assertNotNull("Client did not receive response message,", clientResponseMsg); + assertEquals("Incorrect message received", "Response", ((TextMessage) clientResponseMsg).getText()); } - public void testServerPublishInvalidQueueSuccess() throws AMQException, URLSyntaxException, JMSException, NamingException, Exception + public void setUpClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception { - try - { - Connection conn = getConnection("test", "server", "guest"); - - ((AMQConnection) conn).setConnectionListener(this); - - Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); - - conn.start(); - - MessageProducer sender = ((AMQSession<?, ?>) session).createProducer(null); - - Queue queue = session.createQueue("Invalid"); - - // Send a message that we will wait to be sent, this should give the broker time to close the connection - // before we finish this test. Message is set !immed !mand as the queue is invalid so want to test ACLs not - // queue existence. - ((org.apache.qpid.jms.MessageProducer) sender).send(queue, session.createTextMessage("test"), - DeliveryMode.NON_PERSISTENT, 0, 0L, false, false); - - // Test the connection with a valid consumer - // This may not work as the session may be closed before the queue or consumer creation can occur. - // The correct JMSexception with linked error will only occur when the close method is recevied whilst in - // the failover safe block - session.createConsumer(session.createQueue("example.RequestQueue")).close(); - - //Connection should now be closed and will throw the exception caused by the above send - conn.close(); - - fail("Close is not expected to succeed."); - } - catch (IllegalStateException e) - { - _logger.info("QPID-2345: Session became closed and we got that error rather than the authentication error."); - } - catch (JMSException e) - { - check403Exception(e.getLinkedException()); - } + writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client ALL QUEUE", + "ACL ALLOW-LOG client ALL EXCHANGE"); } - - @Override - public String getConfig() + public void testClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception { - return "config-systests-aclv2.xml"; - } + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); + conn.start(); - @Override - public List<String> getHostList() - { - return Arrays.asList("test", "test2"); + // create kipper + Topic kipper = sess.createTopic("kipper"); + TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper"); + + subscriber.close(); + sess.unsubscribe("kipper"); + + //Do something to show connection is active. + sess.rollback(); + conn.close(); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java deleted file mode 100644 index 290cbfdc14..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalAdminACLTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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.qpid.server.security.acl; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import org.apache.qpid.server.logging.management.LoggingManagementMBean; -import org.apache.qpid.test.utils.JMXTestUtils; - -/** - * Tests that ACLs can be applied to mangement operations that do not correspond to a specific AMQP object. - * - * Theses tests use the logging component, exposed as the {@link LoggingManagementMBean}, to get and set properties. - */ -public class ExternalAdminACLTest extends AbstractACLTestCase -{ - private static final String CATEGORY_PRIORITY = "LogManMBeanTest.category.priority"; - private static final String CATEGORY_LEVEL = "LogManMBeanTest.category.level"; - private static final String LOGGER_LEVEL = "LogManMBeanTest.logger.level"; - - private static final String NEWLINE = System.getProperty("line.separator"); - - private JMXTestUtils _jmx; - private File _testConfigFile; - - @Override - public String getConfig() - { - return "config-systests-aclv2.xml"; - } - - @Override - public List<String> getHostList() - { - return Arrays.asList("global"); - } - - @Override - public void setUp() throws Exception - { - _testConfigFile = createTempTestLog4JConfig(); - - _jmx = new JMXTestUtils(this, "admin", "admin"); - _jmx.setUp(); - super.setUp(); - _jmx.open(); - } - - @Override - public void tearDown() throws Exception - { - _jmx.close(); - super.tearDown(); - } - - private File createTempTestLog4JConfig() - { - File tmpFile = null; - try - { - tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp"); - tmpFile.deleteOnExit(); - - FileWriter fstream = new FileWriter(tmpFile); - BufferedWriter writer = new BufferedWriter(fstream); - - writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE); - writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE); - - writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " + - "threshold=\"null\">"+NEWLINE); - - writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE); - writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE); - writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE); - writer.write(" </layout>"+NEWLINE); - writer.write(" </appender>"+NEWLINE); - - //Example of a 'category' with a 'priority' - writer.write(" <category additivity=\"true\" name=\"" + CATEGORY_PRIORITY +"\">"+NEWLINE); - writer.write(" <priority value=\"info\"/>"+NEWLINE); - writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); - writer.write(" </category>"+NEWLINE); - - //Example of a 'category' with a 'level' - writer.write(" <category additivity=\"true\" name=\"" + CATEGORY_LEVEL +"\">"+NEWLINE); - writer.write(" <level value=\"warn\"/>"+NEWLINE); - writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); - writer.write(" </category>"+NEWLINE); - - //Example of a 'logger' with a 'level' - writer.write(" <logger additivity=\"true\" name=\"" + LOGGER_LEVEL + "\">"+NEWLINE); - writer.write(" <level value=\"error\"/>"+NEWLINE); - writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); - writer.write(" </logger>"+NEWLINE); - - //'root' logger - writer.write(" <root>"+NEWLINE); - writer.write(" <priority value=\"info\"/>"+NEWLINE); - writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE); - writer.write(" </root>"+NEWLINE); - - writer.write("</log4j:configuration>"+NEWLINE); - - writer.flush(); - writer.close(); - } - catch (IOException e) - { - fail("Unable to create temporary test log4j configuration"); - } - - return tmpFile; - } - - public void testGetAllLoggerLevels() throws Exception - { - String[] levels = _jmx.getAvailableLoggerLevels(); - for (int i = 0; i < levels.length; i++) - { - System.out.println(levels[i]); - } - assertEquals("Got incorrect number of log levels", 9, levels.length); - } - - public void testGetAllLoggerLevelsDenied() throws Exception - { - try - { - _jmx.getAvailableLoggerLevels(); - fail("Got list of log levels"); - } - catch (Exception e) - { - // Exception throws - e.printStackTrace(); - assertEquals("Permission denied: Access getAvailableLoggerLevels", e.getMessage()); - } - } - - public void testChangeLoggerLevel() throws Exception - { - String oldLevel = _jmx.getRuntimeRootLoggerLevel(); - System.out.println("old level = " + oldLevel); - _jmx.setRuntimeRootLoggerLevel("DEBUG"); - String newLevel = _jmx.getRuntimeRootLoggerLevel(); - System.out.println("new level = " + newLevel); - assertEquals("Logging level was not changed", "DEBUG", newLevel); - } - - public void testChangeLoggerLevelDenied() throws Exception - { - try - { - _jmx.setRuntimeRootLoggerLevel("DEBUG"); - fail("Logging level was changed"); - } - catch (Exception e) - { - assertEquals("Permission denied: Update setRuntimeRootLoggerLevel", e.getMessage()); - } - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java index 4eb328f091..f2ac590a3c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java @@ -25,6 +25,7 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQSession; import org.apache.qpid.test.utils.FailoverBaseCase; +import org.apache.qpid.test.utils.QpidBrokerTestCase; import javax.jms.Connection; import javax.jms.JMSException; @@ -39,13 +40,12 @@ import javax.naming.NamingException; import java.util.Enumeration; import java.util.Random; -public class QueueBrowserAutoAckTest extends FailoverBaseCase +public class QueueBrowserAutoAckTest extends QpidBrokerTestCase { protected Connection _clientConnection; protected Session _clientSession; protected Queue _queue; protected static final String MESSAGE_ID_PROPERTY = "MessageIDProperty"; - protected boolean CLUSTERED = Boolean.getBoolean("profile.clustered"); public void setUp() throws Exception { @@ -94,22 +94,6 @@ public class QueueBrowserAutoAckTest extends FailoverBaseCase sendMessages(producerConnection, num); } - protected void sendMessages(String connection, int num) throws JMSException - { - Connection producerConnection = null; - try - { - producerConnection = getConnectionFactory(connection).createConnection("guest", "guest"); - } - catch (Exception e) - { - e.printStackTrace(); - fail("Unable to lookup connection in JNDI."); - } - sendMessages(producerConnection, num); - } - - protected void sendMessages(Connection producerConnection, int messageSendCount) throws JMSException { producerConnection.start(); @@ -440,88 +424,4 @@ public class QueueBrowserAutoAckTest extends FailoverBaseCase validate(messages); } - /** - * Testing that a QueueBrowser doesn't actually consume messages from a broker when it fails over. - * @throws JMSException - */ - public void testFailoverWithQueueBrowser() throws JMSException - { - int messages = 5; - - sendMessages("connection1", messages); - if (!CLUSTERED) - { - sendMessages("connection2", messages); - } - - checkQueueDepth(messages); - - _logger.info("Creating Queue Browser"); - QueueBrowser queueBrowser = _clientSession.createBrowser(_queue); - - long queueDepth = 0; - - try - { - queueDepth = ((AMQSession) _clientSession).getQueueDepth((AMQDestination) _queue); - } - catch (AMQException e) - { - fail("Caught exception getting queue depth: " + e.getMessage()); - } - - assertEquals("Session reports Queue depth not as expected", messages, queueDepth); - - int msgCount = 0; - int failPoint = 0; - - failPoint = new Random().nextInt(messages) + 1; - - Enumeration msgs = queueBrowser.getEnumeration(); - while (msgs.hasMoreElements()) - { - msgs.nextElement(); - msgCount++; - - if (msgCount == failPoint) - { - failBroker(getFailingPort()); - } - } - - assertTrue("We should get atleast " + messages + " msgs (found " + msgCount +").", msgCount >= messages); - - if (_logger.isDebugEnabled()) - { - _logger.debug("QBAAT Found " + msgCount + " messages total in browser"); - } - - //Close browser - queueBrowser.close(); - - _logger.info("Closed Queue Browser, validating messages on broker."); - - //Validate all messages still on Broker - validate(messages); - } - - public void testFailoverAsQueueBrowserCreated() throws JMSException - { - // The IoServiceListenerSupport seems to get stuck in with a managedSession that isn't closing when requested. - // So it hangs waiting for the session. - int messages = 50; - - sendMessages("connection1", messages); - if (!CLUSTERED) - { - sendMessages("connection2", messages); - } - - failBroker(getFailingPort()); - - checkQueueDepth(messages); - - //Validate all messages still on Broker 1 - validate(messages); - } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java index 3068deecf8..5b01c28fcc 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java @@ -37,16 +37,13 @@ import javax.naming.NamingException; import org.apache.log4j.Logger; import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.jms.ConnectionURL; import org.apache.qpid.test.utils.FailoverBaseCase; public class FailoverTest extends FailoverBaseCase implements ConnectionListener { private static final Logger _logger = Logger.getLogger(FailoverTest.class); - private static final String QUEUE = "queue"; private static final int DEFAULT_NUM_MESSAGES = 10; private static final int DEFAULT_SEED = 20080921; protected int numMessages = 0; @@ -192,7 +189,6 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener protected void runP2PFailover(int totalMessages, boolean consumeAll, boolean produceAll , boolean transacted) throws JMSException, NamingException { - Message msg = null; int toProduce = totalMessages; _logger.debug("==================================================================="); @@ -281,45 +277,6 @@ public class FailoverTest extends FailoverBaseCase implements ConnectionListener } /** - * The client used to have a fixed timeout of 4 minutes after which failover would no longer work. - * Check that this code has not regressed - * - * @throws Exception if something unexpected occurs in the test. - */ - - public void test4MinuteFailover() throws Exception - { - ConnectionURL connectionURL = getConnectionFactory().getConnectionURL(); - - int RETRIES = 4; - int DELAY = 60000; - - //Set up a long delay on and large number of retries - BrokerDetails details = connectionURL.getBrokerDetails(1); - details.setProperty(BrokerDetails.OPTIONS_RETRY, String.valueOf(RETRIES)); - details.setProperty(BrokerDetails.OPTIONS_CONNECT_DELAY, String.valueOf(DELAY)); - - connection = new AMQConnection(connectionURL); - - ((AMQConnection) connection).setConnectionListener(this); - - //Start the connection - connection.start(); - - long FAILOVER_DELAY = ((long)RETRIES * (long)DELAY); - - // Use Nano seconds as it is more accurate for comparision. - long failTime = System.nanoTime() + FAILOVER_DELAY * 1000000; - - //Fail the first broker - causeFailure(getFailingPort(), FAILOVER_DELAY + DEFAULT_FAILOVER_TIME); - - //Reconnection should occur - assertTrue("Failover did not take long enough", System.nanoTime() > failTime); - } - - - /** * The idea is to run a failover test in a loop by failing over * to the other broker each time. */ diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java index a7efe4922b..2d326d73b8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java @@ -58,7 +58,6 @@ public class JMSDestinationTest extends QpidBrokerTestCase private Connection _connection; private Session _session; - private static final String USER = "admin"; private CountDownLatch _receiveMessage; private Message _message; @@ -143,7 +142,7 @@ public class JMSDestinationTest extends QpidBrokerTestCase public void testMovedToQueue() throws Exception { // Setup JMXUtils - JMXTestUtils jmxUtils = new JMXTestUtils(this, USER, USER); + JMXTestUtils jmxUtils = new JMXTestUtils(this); jmxUtils.setUp(); // Open the JMX Connection jmxUtils.open(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java deleted file mode 100644 index d73d012250..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverOnMessageTest.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * - * 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.qpid.test.unit.ack; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.util.FileUtils; - -import javax.jms.Connection; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.Session; -import javax.jms.TransactionRolledBackException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.io.File; - -/** - * The AcknowlegeAfterFailoverOnMessageTests - * - * Extends the OnMessage AcknowledgeTests to validate that after the client has - * failed over that the client can still receive and ack messages. - * - * All the AcknowledgeTest ack modes are exercised here though some are disabled - * due to know issues (e.g. DupsOk, AutoAck : QPID-143 and the clientAck - * and dirtyClientAck due to QPID-1816) - * - * This class has two main test structures, overrides of AcknowledgeOnMessageTest - * to perform the clean acking based on session ack mode and a series of dirty - * ack tests that test what happends if you receive a message then try and ack - * AFTER you have failed over. - * - * - */ -public class AcknowledgeAfterFailoverOnMessageTest extends AcknowledgeOnMessageTest implements ConnectionListener -{ - - protected CountDownLatch _failoverCompleted = new CountDownLatch(1); - private MessageListener _listener = null; - - @Override - public void setUp() throws Exception - { - super.setUp(); - NUM_MESSAGES = 10; - } - - /** - * Override default init to add connectionListener so we can verify that - * failover took place - * - * @param transacted create a transacted session for this test - * @param mode if not transacted what ack mode to use for this test - * - * @throws Exception if a problem occured during test setup. - */ - @Override - public void init(boolean transacted, int mode) throws Exception - { - super.init(transacted, mode); - ((AMQConnection) _connection).setConnectionListener(this); - // Override the listener for the dirtyAck testing. - if (_listener != null) - { - _consumer.setMessageListener(_listener); - } - } - - /** - * Prepare the broker for the next round. - * - * Called after acknowledging the messsage this method shuts the current - * broker down connnects to the new broker and send a new message for the - * client to failover to and receive. - * - * It ends by restarting the orignal broker so that the cycle can repeat. - * - * When we are able to cluster the java broker then will not need to do the - * message repopulation or QPID_WORK clearing. All that we will need to do - * is send the initial NUM_MESSAGES during startup and then bring the - * brokers down at the right time to cause the client to fail between them. - * - * @param index - * @throws Exception - */ - protected void prepBroker(int index) throws Exception - { - // Alternate killing the broker based on the message index we are at. - - if (index % 2 == 0) - { - failBroker(getFailingPort()); - // Clean up the failed broker - FileUtils.delete(new File(System.getProperty("QPID_WORK") + "/" + getFailingPort()), true); - } - else - { - failBroker(getPort()); - // Clean up the failed broker - FileUtils.delete(new File(System.getProperty("QPID_WORK") + "/" + getPort()), true); - } - - _failoverCompleted = new CountDownLatch(1); - - _logger.info("AAFOMT: prepNewBroker for message send"); - Connection connection = getConnection(); - - try - { - - //Stop the connection whilst we repopulate the broker, or the no_ack - // test will drain the msgs before we can check we put the right number - // back on again. - - Session session = connection.createSession(true, Session.SESSION_TRANSACTED); - // ensure destination is created. - session.createConsumer(_queue).close(); - - - // If this is the last message then we can skip the send. - // But we MUST ensure that we have created the queue with the - // above createConsumer(_queue).close() as the test will end by - // testing the queue depth which will fail if we don't ensure we - // declare the queue. - // index is 0 based so we need to check +1 against NUM_MESSAGES - if ((index + 1) == NUM_MESSAGES) - { - return; - } - - - sendMessage(session, _queue, 1, index + 1, 0); - - // Validate that we have the message on the queue - // In NoAck mode though the messasge may already have been sent to - // the client so we have to skip the vaildation. - if (_consumerSession.getAcknowledgeMode() != AMQSession.NO_ACKNOWLEDGE) - { - assertEquals("Wrong number of messages on queue", 1, - ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); - } - - - } - catch (Exception e) - { - fail("Unable to prep new broker," + e.getMessage()); - } - finally - { - connection.close(); - } - - try - { - - //Restart the broker - if (index % 2 == 0) - { - startBroker(getFailingPort()); - } - else - { - startBroker(getPort()); - } - } - catch (Exception e) - { - fail("Unable to start failover broker," + e.getMessage()); - } - - } - - @Override - public void doAcknowlegement(Message msg) throws JMSException - { - //Acknowledge current message - super.doAcknowlegement(msg); - - try - { - prepBroker(msg.getIntProperty(INDEX)); - } - catch (Exception e) - { - // Provide details of what went wrong with the stack trace - e.printStackTrace(); - fail("Unable to prep new broker," + e); - } - } - - // Instance varilable for DirtyAcking test - int _msgCount = 0; - boolean _cleaned = false; - - class DirtyAckingHandler implements MessageListener - { - /** - * Validate first message but do nothing with it. - * - * Failover - * - * The receive the message again - * - * @param message - */ - public void onMessage(Message message) - { - // Stop processing if we have an error and had to stop running. - if (_receivedAll.getCount() == 0) - { - _logger.debug("Dumping msgs due to error(" + _causeOfFailure.get().getMessage() + "):" + message); - return; - } - - try - { - // Check we have the next message as expected - assertNotNull("Message " + _msgCount + " not correctly received.", message); - assertEquals("Incorrect message received", _msgCount, message.getIntProperty(INDEX)); - - if (_msgCount == 0 && _failoverCompleted.getCount() != 0) - { - // This is the first message we've received so lets fail the broker - - failBroker(getFailingPort()); - - repopulateBroker(); - - _logger.error("Received first msg so failing over"); - - return; - } - - _msgCount++; - - // Don't acknowlege the first message after failover so we can commit - // them together - if (_msgCount == 1) - { - _logger.error("Received first msg after failover ignoring:" + _msgCount); - - // Acknowledge the first message if we are now on the cleaned pass - if (_cleaned) - { - _receivedAll.countDown(); - } - - return; - } - - if (_consumerSession.getTransacted()) - { - try - { - _consumerSession.commit(); - if (!_cleaned) - { - fail("Session is dirty we should get an TransactionRolledBackException"); - } - } - catch (TransactionRolledBackException trbe) - { - //expected path - } - } - else - { - try - { - message.acknowledge(); - if (!_cleaned) - { - fail("Session is dirty we should get an IllegalStateException"); - } - } - catch (javax.jms.IllegalStateException ise) - { - assertEquals("Incorrect Exception thrown", "has failed over", ise.getMessage()); - // Recover the sesion and try again. - _consumerSession.recover(); - } - } - - // Acknowledge the last message if we are in a clean state - // this will then trigger test teardown. - if (_cleaned) - { - _receivedAll.countDown(); - } - - //Reset message count so we can try again. - _msgCount = 0; - _cleaned = true; - } - catch (Exception e) - { - // If something goes wrong stop and notifiy main thread. - fail(e); - } - } - } - - /** - * Test that Acking/Committing a message received before failover causes - * an exception at commit/ack time. - * - * Expected behaviour is that in: - * * tx mode commit() throws a transacted RolledBackException - * * client ack mode throws an IllegalStateException - * - * @param transacted is this session trasacted - * @param mode What ack mode should be used if not trasacted - * - * @throws Exception if something goes wrong. - */ - protected void testDirtyAcking(boolean transacted, int mode) throws Exception - { - NUM_MESSAGES = 2; - _listener = new DirtyAckingHandler(); - - super.testAcking(transacted, mode); - } - - public void testDirtyClientAck() throws Exception - { - testDirtyAcking(false, Session.CLIENT_ACKNOWLEDGE); - } - - public void testDirtyAckingTransacted() throws Exception - { - testDirtyAcking(true, Session.SESSION_TRANSACTED); - } - - private void repopulateBroker() throws Exception - { - // Repopulate this new broker so we can test what happends after failover - - //Get the connection to the first (main port) broker. - Connection connection = getConnection(); - // Use a transaction to send messages so we can be sure they arrive. - Session session = connection.createSession(true, Session.SESSION_TRANSACTED); - // ensure destination is created. - session.createConsumer(_queue).close(); - - sendMessage(session, _queue, NUM_MESSAGES); - - assertEquals("Wrong number of messages on queue", NUM_MESSAGES, - ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); - - connection.close(); - } - - // AMQConnectionListener Interface.. used so we can validate that we - // actually failed over. - - public void bytesSent(long count) - { - } - - public void bytesReceived(long count) - { - } - - public boolean preFailover(boolean redirect) - { - //Allow failover - return true; - } - - public boolean preResubscribe() - { - //Allow failover - return true; - } - - public void failoverComplete() - { - _failoverCompleted.countDown(); - } - - /** - * Override so we can block until failover has completd - * - * @param port - */ - @Override - public void failBroker(int port) - { - super.failBroker(port); - - try - { - if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS)) - { - // Use an exception so that we use our local fail() that notifies the main thread of failure - throw new Exception("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME); - } - - } - catch (Exception e) - { - // Use an exception so that we use our local fail() that notifies the main thread of failure - fail(e); - } - } - -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java deleted file mode 100644 index acc7d5a4c1..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeAfterFailoverTest.java +++ /dev/null @@ -1,317 +0,0 @@ -/* -* -* 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.qpid.test.unit.ack; - -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.util.FileUtils; - -import javax.jms.Connection; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import javax.jms.TransactionRolledBackException; -import java.io.File; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * - */ -public class AcknowledgeAfterFailoverTest extends AcknowledgeTest implements ConnectionListener -{ - - protected CountDownLatch _failoverCompleted = new CountDownLatch(1); - - @Override - public void setUp() throws Exception - { - super.setUp(); - // This must be even for the test to run correctly. - // Otherwise we will kill the standby broker - // not the one we are connected to. - // The test will still pass but it will not be exactly - // as described. - NUM_MESSAGES = 6; - } - - /** - * Override default init to add connectionListener so we can verify that - * failover took place - * - * @param transacted create a transacted session for this test - * @param mode if not transacted what ack mode to use for this test - * @throws Exception if a problem occured during test setup. - */ - @Override - protected void init(boolean transacted, int mode) throws Exception - { - super.init(transacted, mode); - ((AMQConnection) _connection).setConnectionListener(this); - } - - protected void prepBroker(int index) throws Exception - { - // If this is the last message then we can skip the prep. - if (index == NUM_MESSAGES) - { - return; - } - - if (index % 2 == 0) - { - failBroker(getFailingPort()); - // Clean up the failed broker - FileUtils.delete(new File(System.getProperty("QPID_WORK") + "/" + getFailingPort()), true); - } - else - { - failBroker(getPort()); - // Clean up the failed broker - FileUtils.delete(new File(System.getProperty("QPID_WORK") + "/" + getPort()), true); - } - - // Ensure we have the right data on the broker - Connection connection = getConnection(); - Session session = connection.createSession(true, Session.SESSION_TRANSACTED); - // ensure destination is created. - session.createConsumer(_queue).close(); - - sendMessage(session, _queue, 1, index + 1, 0); - - if (_consumerSession.getAcknowledgeMode() != AMQSession.NO_ACKNOWLEDGE) - { - assertEquals("Wrong number of messages on queue", 1, - ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); - } - - connection.close(); - - try - { - if (index % 2 == 0) - { - startBroker(getFailingPort()); - } - else - { - startBroker(getPort()); - } - } - catch (Exception e) - { - fail("Unable to start failover broker," + e.getMessage()); - } - } - - @Override - public void doAcknowlegement(Message msg) throws JMSException - { - //Acknowledge current message - super.doAcknowlegement(msg); - - try - { - prepBroker(msg.getIntProperty(INDEX)); - } - catch (Exception e) - { - fail("Unable to prep new broker," + e.getMessage()); - } - - } - - /** - * Test that Acking/Committing a message received before failover causes - * an exception at commit/ack time. - * <p/> - * Expected behaviour is that in: - * * tx mode commit() throws a transacted RolledBackException - * * client ack mode throws an IllegalStateException - * - * @param transacted is this session trasacted - * @param mode What ack mode should be used if not trasacted - * @throws Exception if something goes wrong. - */ - protected void testDirtyAcking(boolean transacted, int mode) throws Exception - { - NUM_MESSAGES = 2; - //Test Dirty Failover Fails - init(transacted, mode); - - _connection.start(); - - Message msg = _consumer.receive(1500); - - int count = 0; - assertNotNull("Message " + count + " not correctly received.", msg); - assertEquals("Incorrect message received", count, msg.getIntProperty(INDEX)); - - //Don't acknowledge just prep the next broker. Without changing count - // Prep the new broker to have all all the messages so we can validate - // that they can all be correctly received. - try - { - - //Stop the connection so we can validate the number of message count - // on the queue is correct after failover - _connection.stop(); - failBroker(getFailingPort()); - - //Get the connection to the first (main port) broker. - Connection connection = getConnection();//getConnectionFactory("connection1").getConnectionURL()); - // Use a transaction to send messages so we can be sure they arrive. - Session session = connection.createSession(true, Session.SESSION_TRANSACTED); - // ensure destination is created. - session.createConsumer(_queue).close(); - - sendMessage(session, _queue, NUM_MESSAGES); - - assertEquals("Wrong number of messages on queue", NUM_MESSAGES, - ((AMQSession) session).getQueueDepth((AMQDestination) _queue)); - - connection.close(); - - //restart connection - _connection.start(); - } - catch (Exception e) - { - fail("Unable to prep new broker," + e.getMessage()); - } - - // Consume the next message - don't check what it is as a normal would - // assume it is msg 1 but as we've fallen over it is msg 0 again. - msg = _consumer.receive(1500); - - if (_consumerSession.getTransacted()) - { - try - { - _consumerSession.commit(); - fail("Session is dirty we should get an TransactionRolledBackException"); - } - catch (TransactionRolledBackException trbe) - { - //expected path - } - } - else - { - try - { - msg.acknowledge(); - fail("Session is dirty we should get an IllegalStateException"); - } - catch (javax.jms.IllegalStateException ise) - { - assertEquals("Incorrect Exception thrown", "has failed over", ise.getMessage()); - // Recover the sesion and try again. - _consumerSession.recover(); - } - } - - msg = _consumer.receive(1500); - // Validate we now get the first message back - assertEquals(0, msg.getIntProperty(INDEX)); - - msg = _consumer.receive(1500); - // and the second message - assertEquals(1, msg.getIntProperty(INDEX)); - - // And now verify that we can now commit the clean session - if (_consumerSession.getTransacted()) - { - _consumerSession.commit(); - } - else - { - msg.acknowledge(); - } - - assertEquals("Wrong number of messages on queue", 0, - ((AMQSession) _consumerSession).getQueueDepth((AMQDestination) _queue)); - } - - public void testDirtyClientAck() throws Exception - { - testDirtyAcking(false, Session.CLIENT_ACKNOWLEDGE); - } - - public void testDirtyAckingTransacted() throws Exception - { - testDirtyAcking(true, Session.SESSION_TRANSACTED); - } - - // AMQConnectionListener Interface.. used so we can validate that we - // actually failed over. - - public void bytesSent(long count) - { - } - - public void bytesReceived(long count) - { - } - - public boolean preFailover(boolean redirect) - { - //Allow failover - return true; - } - - public boolean preResubscribe() - { - //Allow failover - return true; - } - - public void failoverComplete() - { - _failoverCompleted.countDown(); - } - - /** - * Override so we can block until failover has completd - * - * @param port - */ - @Override - public void failBroker(int port) - { - super.failBroker(port); - - try - { - if (!_failoverCompleted.await(DEFAULT_FAILOVER_TIME, TimeUnit.MILLISECONDS)) - { - fail("Failover did not occur in specified time:" + DEFAULT_FAILOVER_TIME); - } - } - catch (InterruptedException e) - { - fail("Failover was interrupted"); - } - } - -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java index efea57e5d2..12039caf25 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java @@ -23,7 +23,7 @@ package org.apache.qpid.test.unit.ack; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.utils.FailoverBaseCase; +import org.apache.qpid.test.utils.QpidBrokerTestCase; import javax.jms.Connection; import javax.jms.JMSException; @@ -39,7 +39,7 @@ import javax.jms.MessageProducer; * * The ack mode is provided from the various test methods. */ -public class AcknowledgeTest extends FailoverBaseCase +public class AcknowledgeTest extends QpidBrokerTestCase { protected int NUM_MESSAGES; protected Connection _connection; diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java deleted file mode 100644 index 13c78c1e14..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/QuickAcking.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * - * 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.qpid.test.unit.ack; - -import java.util.concurrent.CountDownLatch; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.jms.ConnectionListener; -import org.apache.qpid.test.utils.QpidBrokerTestCase; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Queue; -import javax.jms.Session; - -/** - * This is a quick manual test to validate acking after failover with a - * transacted session. - * - * Start an external broker then run this test. Std Err will print. - * Sent Message: 1 - * Received Message: 1 - * - * You can then restart the external broker, which will cause failover, which - * will be complete when the following appears. - * - * Failover Complete - * - * A second message send/receive cycle is then done to validate that the - * connection/session are still working. - * - */ -public class QuickAcking extends QpidBrokerTestCase implements ConnectionListener -{ - protected AMQConnection _connection; - protected Queue _queue; - protected Session _session; - protected MessageConsumer _consumer; - private CountDownLatch _failedOver; - private static final String INDEX = "INDEX"; - private int _count = 0; - - public void setUp() - { - // Prevent broker startup. Broker must be run manually. - } - - public void test() throws Exception - { - _failedOver = new CountDownLatch(1); - - _connection = new AMQConnection("amqp://guest:guest@client/test?brokerlist='localhost?retries='20'&connectdelay='2000''"); - - _session = _connection.createSession(true, Session.SESSION_TRANSACTED); - _queue = _session.createQueue("QAtest"); - _consumer = _session.createConsumer(_queue); - _connection.setConnectionListener(this); - _connection.start(); - - sendAndReceive(); - - _failedOver.await(); - - sendAndReceive(); - - } - - private void sendAndReceive() - throws Exception - { - sendMessage(); - - Message message = _consumer.receive(); - - if (message.getIntProperty(INDEX) != _count) - { - throw new Exception("Incorrect message recieved:" + _count); - } - - if (_session.getTransacted()) - { - _session.commit(); - } - System.err.println("Recevied Message:" + _count); - } - - private void sendMessage() throws JMSException - { - MessageProducer producer = _session.createProducer(_queue); - Message message = _session.createMessage(); - _count++; - message.setIntProperty(INDEX, _count); - - producer.send(message); - if (_session.getTransacted()) - { - _session.commit(); - } - producer.close(); - - System.err.println("Sent Message:" + _count); - } - - public void bytesSent(long count) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void bytesReceived(long count) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean preFailover(boolean redirect) - { - return true; - } - - public boolean preResubscribe() - { - return true; - } - - public void failoverComplete() - { - System.err.println("Failover Complete"); - _failedOver.countDown(); - } -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java index 66ca1d8345..2fd3811cb4 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java @@ -21,13 +21,14 @@ package org.apache.qpid.test.unit.ack; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.Session; import org.apache.qpid.test.utils.FailoverBaseCase; +import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.jms.BytesMessage; import javax.jms.Connection; import javax.jms.Destination; import javax.jms.JMSException; @@ -38,14 +39,15 @@ import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.TextMessage; -import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -public class RecoverTest extends FailoverBaseCase +public class RecoverTest extends QpidBrokerTestCase { static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class); + private static final int POSIITIVE_TIMEOUT = 2000; + private volatile Exception _error; private AtomicInteger count; @@ -64,7 +66,7 @@ public class RecoverTest extends FailoverBaseCase protected void initTest() throws Exception { - _connection = (AMQConnection) getConnection("guest", "guest"); + _connection = (AMQConnection) getConnection(); _consumerSession = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); Queue queue = _consumerSession.createQueue(getTestQueueName()); @@ -174,7 +176,7 @@ public class RecoverTest extends FailoverBaseCase public void testAcknowledgePerConsumer() throws Exception { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con = (AMQConnection) getConnection(); Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); Queue queue = @@ -186,7 +188,7 @@ public class RecoverTest extends FailoverBaseCase MessageConsumer consumer = consumerSession.createConsumer(queue); MessageConsumer consumer2 = consumerSession.createConsumer(queue2); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con2 = (AMQConnection) getConnection(); Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); MessageProducer producer = producerSession.createProducer(queue); MessageProducer producer2 = producerSession.createProducer(queue2); @@ -216,7 +218,7 @@ public class RecoverTest extends FailoverBaseCase public void testRecoverInAutoAckListener() throws Exception { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con = (AMQConnection) getConnection(); final Session consumerSession = con.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = @@ -304,16 +306,6 @@ public class RecoverTest extends FailoverBaseCase _error = e; } - private void sendMessages(javax.jms.Session session,Destination dest,int count) throws Exception - { - MessageProducer prod = session.createProducer(dest); - for (int i=0; i<count; i++) - { - prod.send(session.createTextMessage("Msg" + i)); - } - prod.close(); - } - /** * Goal : Check if ordering is preserved when doing recovery under reasonable circumstances. * Refer QPID-2471 for more details. @@ -325,48 +317,47 @@ public class RecoverTest extends FailoverBaseCase * While doing so it will verify that the messages are not * delivered out of order. */ - public void testOderingWithSyncConsumer() throws Exception + public void testOrderingWithSyncConsumer() throws Exception { - Connection con = (Connection) getConnection("guest", "guest"); + Connection con = (Connection) getConnection(); javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); Destination topic = session.createTopic("myTopic"); MessageConsumer cons = session.createConsumer(topic); - sendMessages(session,topic,8); + sendMessage(session,topic,8); con.start(); - - int messageSeen = 0; - int expectedMsg = 0; + int messageSeen = 0; + int expectedIndex = 0; long startTime = System.currentTimeMillis(); - while(expectedMsg < 8) + while(expectedIndex < 8) { // Based on historical data, on average the test takes about 6 secs to complete. if (System.currentTimeMillis() - startTime > 8000) { fail("Test did not complete on time. Received " + - expectedMsg + " msgs so far. Please check the logs"); + expectedIndex + " msgs so far. Please check the logs"); } - Message message = cons.receive(2000); - String text=((TextMessage) message).getText(); + Message message = cons.receive(POSIITIVE_TIMEOUT); + int actualIndex = message.getIntProperty(INDEX); - assertEquals("Received Message Out Of Order","Msg"+expectedMsg,text); + assertEquals("Received Message Out Of Order",expectedIndex, actualIndex); //don't ack the message until we receive it 5 times if( messageSeen < 5 ) { - _logger.debug("Ignoring message " + text + " and calling recover"); + _logger.debug("Ignoring message " + actualIndex + " and calling recover"); session.recover(); messageSeen++; } else { messageSeen = 0; - expectedMsg++; + expectedIndex++; message.acknowledge(); - _logger.debug("Acknowledging message " + text); + _logger.debug("Acknowledging message " + actualIndex); } } } @@ -377,44 +368,45 @@ public class RecoverTest extends FailoverBaseCase * Same as testOderingWithSyncConsumer but using a * Message Listener instead of a sync receive(). */ - public void testOderingWithAsyncConsumer() throws Exception + public void testOrderingWithAsyncConsumer() throws Exception { - Connection con = (Connection) getConnection("guest", "guest"); + Connection con = (Connection) getConnection(); final javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); Destination topic = session.createTopic("myTopic"); MessageConsumer cons = session.createConsumer(topic); - sendMessages(session,topic,8); + sendMessage(session,topic,8); con.start(); - + final Object lock = new Object(); final AtomicBoolean pass = new AtomicBoolean(false); //used as work around for 'final' + cons.setMessageListener(new MessageListener() { int messageSeen = 0; - int expectedMsg = 0; - + int expectedIndex = 0; + public void onMessage(Message message) { try { - String text = ((TextMessage) message).getText(); - assertEquals("Received Message Out Of Order","Msg"+expectedMsg,text); + int actualIndex = message.getIntProperty(INDEX); + assertEquals("Received Message Out Of Order", expectedIndex, actualIndex); //don't ack the message until we receive it 5 times if( messageSeen < 5 ) { - _logger.debug("Ignoring message " + text + " and calling recover"); + _logger.debug("Ignoring message " + actualIndex + " and calling recover"); session.recover(); messageSeen++; } else { messageSeen = 0; - expectedMsg++; + expectedIndex++; message.acknowledge(); - _logger.debug("Acknowledging message " + text); - if (expectedMsg == 8) + _logger.debug("Acknowledging message " + actualIndex); + if (expectedIndex == 8) { pass.set(true); synchronized (lock) @@ -426,7 +418,7 @@ public class RecoverTest extends FailoverBaseCase } catch (JMSException e) { - fail("Exception : " + e.getMessage()); + _error = e; synchronized (lock) { lock.notifyAll(); @@ -440,10 +432,53 @@ public class RecoverTest extends FailoverBaseCase // Based on historical data, on average the test takes about 6 secs to complete. lock.wait(8000); } - + + assertNull("Unexpected exception thrown by async listener", _error); + if (!pass.get()) { fail("Test did not complete on time. Please check the logs"); } } + + /** + * This test ensures that after exhausting credit (prefetch), a {@link Session#recover()} successfully + * restores credit and allows the same messages to be re-received. + */ + public void testRecoverSessionAfterCreditExhausted() throws Exception + { + final int maxPrefetch = 5; + + // We send more messages than prefetch size. This ensure that if the 0-10 client were to + // complete the message commands before the rollback command is sent, the broker would + // send additional messages utilising the release credit. This problem would manifest itself + // as an incorrect message (or no message at all) being received at the end of the test. + + final int numMessages = maxPrefetch * 2; + + setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, String.valueOf(maxPrefetch)); + + Connection con = (Connection) getConnection(); + final javax.jms.Session session = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Destination dest = session.createQueue(getTestQueueName()); + MessageConsumer cons = session.createConsumer(dest); + + sendMessage(session, dest, numMessages); + con.start(); + + for (int i=0; i< maxPrefetch; i++) + { + final Message message = cons.receive(POSIITIVE_TIMEOUT); + assertNotNull("Received:" + i, message); + assertEquals("Unexpected message received", i, message.getIntProperty(INDEX)); + } + + _logger.info("Recovering"); + session.recover(); + + Message result = cons.receive(POSIITIVE_TIMEOUT); + // Expect the first message + assertEquals("Unexpected message received", 0, result.getIntProperty(INDEX)); + } + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java index 57ff6a4fa2..474a425b28 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java @@ -54,7 +54,14 @@ public class AMQConnectionTest extends QpidBrokerTestCase _topic = new AMQTopic(_connection.getDefaultTopicExchangeName(), new AMQShortString("mytopic")); _queue = new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("myqueue")); } - + + @Override + protected void tearDown() throws Exception + { + _connection.close(); + super.tearDown(); //To change body of overridden methods use File | Settings | File Templates. + } + protected void createConnection() throws Exception { _connection = (AMQConnection) getConnection("guest", "guest"); @@ -67,16 +74,27 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testCreateQueueSession() throws JMSException { - _queueSession = _connection.createQueueSession(false, AMQSession.NO_ACKNOWLEDGE); + createQueueSession(); + } + + private void createQueueSession() throws JMSException + { + _queueSession = _connection.createQueueSession(false, AMQSession.NO_ACKNOWLEDGE); } public void testCreateTopicSession() throws JMSException { + createTopicSession(); + } + + private void createTopicSession() throws JMSException + { _topicSession = _connection.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); } public void testTopicSessionCreateBrowser() throws JMSException { + createTopicSession(); try { _topicSession.createBrowser(_queue); @@ -94,6 +112,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testTopicSessionCreateQueue() throws JMSException { + createTopicSession(); try { _topicSession.createQueue("abc"); @@ -111,6 +130,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testTopicSessionCreateTemporaryQueue() throws JMSException { + createTopicSession(); try { _topicSession.createTemporaryQueue(); @@ -128,6 +148,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testQueueSessionCreateTemporaryTopic() throws JMSException { + createQueueSession(); try { _queueSession.createTemporaryTopic(); @@ -145,6 +166,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testQueueSessionCreateTopic() throws JMSException { + createQueueSession(); try { _queueSession.createTopic("abc"); @@ -162,6 +184,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testQueueSessionDurableSubscriber() throws JMSException { + createQueueSession(); try { _queueSession.createDurableSubscriber(_topic, "abc"); @@ -179,6 +202,7 @@ public class AMQConnectionTest extends QpidBrokerTestCase public void testQueueSessionUnsubscribe() throws JMSException { + createQueueSession(); try { _queueSession.unsubscribe("abc"); @@ -243,25 +267,6 @@ public class AMQConnectionTest extends QpidBrokerTestCase assertNotNull("Consumer B should have received the message",msg); } - public void testGetChannelID() throws Exception - { - long maxChannelID = _connection.getMaximumChannelCount(); - if (isBroker010()) - { - //Usable numbers are 0 to N-1 when using 0-10 - //and 1 to N for 0-8/0-9 - maxChannelID = maxChannelID-1; - } - for (int j = 0; j < 3; j++) - { - int i = isBroker010() ? 0 : 1; - for ( ; i <= maxChannelID; i++) - { - int id = _connection.getNextChannelID(); - assertEquals("Unexpected number on iteration "+j, i, id); - _connection.deregisterSession(id); - } - } - } + } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java new file mode 100644 index 0000000000..e59dac8c01 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java @@ -0,0 +1,695 @@ +/* + * + * 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.qpid.test.unit.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.configuration.ClientProperties; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Test that the MaxRedelivery feature works as expected, allowing the client to reject + * messages during rollback/recover whilst specifying they not be requeued if delivery + * to an application has been attempted a specified number of times. + * + * General approach: specify a set of messages which will cause the test client to then + * deliberately rollback/recover the session after consuming, and monitor that they are + * re-delivered the specified number of times before the client rejects them without requeue + * and then verify that they are not subsequently redelivered. + * + * Additionally, the queue used in the test is configured for DLQ'ing, and the test verifies + * that the messages rejected without requeue are then present on the appropriate DLQ. + */ +public class MaxDeliveryCountTest extends QpidBrokerTestCase +{ + private static final Logger _logger = Logger.getLogger(MaxDeliveryCountTest.class); + private boolean _failed; + private String _failMsg; + private static final int MSG_COUNT = 15; + private static final int MAX_DELIVERY_COUNT = 2; + private CountDownLatch _awaitCompletion; + + public void setUp() throws Exception + { + //enable DLQ/maximumDeliveryCount support for all queues at the vhost level + setConfigurationProperty("virtualhosts.virtualhost.test.queues.maximumDeliveryCount", + String.valueOf(MAX_DELIVERY_COUNT)); + setConfigurationProperty("virtualhosts.virtualhost.test.queues.deadLetterQueues", + String.valueOf(true)); + + //Ensure management is on + setConfigurationProperty("management.enabled", "true"); + setConfigurationProperty("management.ssl.enabled", "false"); + + // Set client-side flag to allow the server to determine if messages + // dead-lettered or requeued. + setTestClientSystemProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, "server"); + + super.setUp(); + + boolean durableSub = isDurSubTest(); + + //declare the test queue + Connection consumerConnection = getConnection(); + Session consumerSession = consumerConnection.createSession(false,Session.AUTO_ACKNOWLEDGE); + Destination destination = getDestination(consumerSession, durableSub); + if(durableSub) + { + consumerSession.createDurableSubscriber((Topic)destination, getName()).close(); + } + else + { + consumerSession.createConsumer(destination).close(); + } + + consumerConnection.close(); + + //Create Producer put some messages on the queue + Connection producerConnection = getConnection(); + producerConnection.start(); + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(getDestination(producerSession, durableSub)); + + for (int count = 1; count <= MSG_COUNT; count++) + { + Message msg = producerSession.createTextMessage(generateContent(count)); + msg.setIntProperty("count", count); + producer.send(msg); + } + + producerConnection.close(); + + _failed = false; + _awaitCompletion = new CountDownLatch(1); + } + + private Destination getDestination(Session consumerSession, boolean durableSub) throws JMSException + { + if(durableSub) + { + return consumerSession.createTopic(getTestQueueName()); + } + else + { + return consumerSession.createQueue(getTestQueueName()); + } + } + + private String generateContent(int count) + { + return "Message " + count + " content."; + } + + /** + * Test that Max Redelivery is enforced when using onMessage() on a + * Client-Ack session. + */ + public void testAsynchronousClientAckSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.CLIENT_ACKNOWLEDGE, redeliverMsgs, false, false); + } + + /** + * Test that Max Redelivery is enforced when using onMessage() on a + * transacted session. + */ + public void testAsynchronousTransactedSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.SESSION_TRANSACTED, redeliverMsgs, false, false); + } + + /** + * Test that Max Redelivery is enforced when using onMessage() on an + * Auto-Ack session. + */ + public void testAsynchronousAutoAckSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.AUTO_ACKNOWLEDGE, redeliverMsgs, false, false); + } + + /** + * Test that Max Redelivery is enforced when using onMessage() on a + * Dups-OK session. + */ + public void testAsynchronousDupsOkSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.DUPS_OK_ACKNOWLEDGE, redeliverMsgs, false, false); + } + + /** + * Test that Max Redelivery is enforced when using recieve() on a + * Client-Ack session. + */ + public void testSynchronousClientAckSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(3); + redeliverMsgs.add(14); + + doTest(Session.CLIENT_ACKNOWLEDGE, redeliverMsgs, true, false); + } + + /** + * Test that Max Redelivery is enforced when using recieve() on a + * transacted session. + */ + public void testSynchronousTransactedSession() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.SESSION_TRANSACTED, redeliverMsgs, true, false); + } + + public void testDurableSubscription() throws Exception + { + final ArrayList<Integer> redeliverMsgs = new ArrayList<Integer>(); + redeliverMsgs.add(1); + redeliverMsgs.add(2); + redeliverMsgs.add(5); + redeliverMsgs.add(14); + + doTest(Session.SESSION_TRANSACTED, redeliverMsgs, false, true); + } + + public void doTest(final int deliveryMode, final ArrayList<Integer> redeliverMsgs, final boolean synchronous, final boolean durableSub) throws Exception + { + final Connection clientConnection = getConnection(); + + final boolean transacted = deliveryMode == Session.SESSION_TRANSACTED ? true : false; + final Session clientSession = clientConnection.createSession(transacted, deliveryMode); + + MessageConsumer consumer; + Destination dest = getDestination(clientSession, durableSub); + AMQQueue checkQueue; + if(durableSub) + { + consumer = clientSession.createDurableSubscriber((Topic)dest, getName()); + checkQueue = new AMQQueue("amq.topic", "clientid" + ":" + getName()); + } + else + { + consumer = clientSession.createConsumer(dest); + checkQueue = (AMQQueue) dest; + } + + assertEquals("The queue should have " + MSG_COUNT + " msgs at start", + MSG_COUNT, ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueue)); + + clientConnection.start(); + + int expectedDeliveries = MSG_COUNT + ((MAX_DELIVERY_COUNT -1) * redeliverMsgs.size()); + + if(synchronous) + { + doSynchronousTest(clientSession, consumer, clientSession.getAcknowledgeMode(), + MAX_DELIVERY_COUNT, expectedDeliveries, redeliverMsgs); + } + else + { + addMessageListener(clientSession, consumer, clientSession.getAcknowledgeMode(), + MAX_DELIVERY_COUNT, expectedDeliveries, redeliverMsgs); + + try + { + if (!_awaitCompletion.await(20, TimeUnit.SECONDS)) + { + fail("Test did not complete in 20 seconds."); + } + } + catch (InterruptedException e) + { + fail("Unable to wait for test completion"); + throw e; + } + + if(_failed) + { + fail(_failMsg); + } + } + consumer.close(); + + //check the source queue is now empty + assertEquals("The queue should have 0 msgs left", 0, ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueue)); + + //check the DLQ has the required number of rejected-without-requeue messages + verifyDLQdepth(redeliverMsgs.size(), clientSession, durableSub); + + if(isBrokerStorePersistent()) + { + //restart the broker to verify persistence of the DLQ and the messages on it + clientConnection.close(); + + restartBroker(); + + final Connection clientConnection2 = getConnection(); + final Session clientSession2 = clientConnection2.createSession(transacted, deliveryMode); + clientConnection2.start(); + + //verify the messages on the DLQ + verifyDLQcontent(clientConnection2, redeliverMsgs, getTestQueueName(), durableSub); + clientConnection2.close(); + } + else + { + + //verify the messages on the DLQ + verifyDLQcontent(clientConnection, redeliverMsgs, getTestQueueName(), durableSub); + clientConnection.close(); + } + + } + + private void verifyDLQdepth(int expected, Session clientSession, boolean durableSub) throws AMQException + { + AMQDestination checkQueueDLQ; + if(durableSub) + { + checkQueueDLQ = new AMQQueue("amq.topic", "clientid" + ":" + getName() + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + else + { + checkQueueDLQ = new AMQQueue("amq.direct", getTestQueueName() + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + + assertEquals("The DLQ should have " + expected + " msgs on it", expected, + ((AMQSession<?,?>) clientSession).getQueueDepth(checkQueueDLQ)); + } + + private void verifyDLQcontent(Connection clientConnection, List<Integer> redeliverMsgs, String destName, boolean durableSub) throws JMSException + { + Session clientSession = clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageConsumer consumer; + if(durableSub) + { + if (isBroker010()) + { + consumer = clientSession.createConsumer(clientSession.createQueue("clientid:" +getName() + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX)); + } + else + { + consumer = clientSession.createDurableSubscriber(clientSession.createTopic(destName), getName() + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + } + else + { + consumer = clientSession.createConsumer( + clientSession.createQueue(destName + AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX)); + } + + //keep track of the message we expect to still be on the DLQ + List<Integer> outstandingMessages = new ArrayList<Integer>(redeliverMsgs); + int numMsg = outstandingMessages.size(); + + for(int i = 0; i < numMsg; i++) + { + Message message = consumer.receive(250); + + assertNotNull("failed to consume expected message " + i + " from DLQ", message); + assertTrue("message " + i + " was the wrong type", message instanceof TextMessage); + + //using Integer here to allow removing the value from the list, using int + //would instead result in removal of the element at that index + Integer msgId = message.getIntProperty("count"); + + TextMessage txt = (TextMessage) message; + _logger.info("Received message " + msgId + " at " + i + " from the DLQ: " + txt.getText()); + + assertTrue("message " + i + " was not one of those which should have been on the DLQ", + redeliverMsgs.contains(msgId)); + assertTrue("message " + i + " was not one of those expected to still be on the DLQ", + outstandingMessages.contains(msgId)); + assertEquals("Message " + i + " content was not as expected", generateContent(msgId), txt.getText()); + + //remove from the list of outstanding msgs + outstandingMessages.remove(msgId); + } + + if(outstandingMessages.size() > 0) + { + String failures = ""; + for(Integer msg : outstandingMessages) + { + failures = failures.concat(msg + " "); + } + fail("some DLQ'd messages were not found on the DLQ: " + failures); + } + } + + private void addMessageListener(final Session session, final MessageConsumer consumer, final int deliveryMode, final int maxDeliveryCount, + final int expectedTotalNumberOfDeliveries, final ArrayList<Integer> redeliverMsgs) throws JMSException + { + if(deliveryMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE + || deliveryMode == org.apache.qpid.jms.Session.PRE_ACKNOWLEDGE) + { + failAsyncTest("Max Delivery feature is not supported with this acknowledgement mode" + + "when using asynchronous message delivery."); + } + + consumer.setMessageListener(new MessageListener() + { + private int _deliveryAttempts = 0; //number of times given message(s) have been seen + private int _numMsgsToBeRedelivered = 0; //number of messages to rollback/recover + private int _totalNumDeliveries = 0; + private int _expectedMessage = 1; + + public void onMessage(Message message) + { + if(_failed || _awaitCompletion.getCount() == 0L) + { + //don't process anything else + return; + } + + _totalNumDeliveries++; + + if (message == null) + { + failAsyncTest("Should not get null messages"); + return; + } + + try + { + int msgId = message.getIntProperty("count"); + + _logger.info("Received message: " + msgId); + + //check the message is the one we expected + if(_expectedMessage != msgId) + { + failAsyncTest("Expected message " + _expectedMessage + " , got message " + msgId); + return; + } + + _expectedMessage++; + + //keep track of the overall deliveries to ensure we don't see more than expected + if(_totalNumDeliveries > expectedTotalNumberOfDeliveries) + { + failAsyncTest("Expected total of " + expectedTotalNumberOfDeliveries + + " message deliveries, reached " + _totalNumDeliveries); + } + + //check if this message is one chosen to be rolled back / recovered + if(redeliverMsgs.contains(msgId)) + { + _numMsgsToBeRedelivered++; + + //check if next message is going to be rolled back / recovered too + if(redeliverMsgs.contains(msgId +1)) + { + switch(deliveryMode) + { + case Session.SESSION_TRANSACTED: + //skip on to next message immediately + return; + case Session.CLIENT_ACKNOWLEDGE: + //skip on to next message immediately + return; + case Session.DUPS_OK_ACKNOWLEDGE: + //fall through + case Session.AUTO_ACKNOWLEDGE: + //must recover session now or onMessage will ack, so + //just fall through the if + break; + } + } + + _deliveryAttempts++; //increment count of times the current rolled back/recovered message(s) have been seen + + _logger.debug("ROLLBACK/RECOVER"); + switch(deliveryMode) + { + case Session.SESSION_TRANSACTED: + session.rollback(); + break; + case Session.CLIENT_ACKNOWLEDGE: + //fall through + case Session.DUPS_OK_ACKNOWLEDGE: + //fall through + case Session.AUTO_ACKNOWLEDGE: + session.recover(); + break; + } + + if( _deliveryAttempts >= maxDeliveryCount) + { + //the client should have rejected the latest messages upon then + //above recover/rollback, adjust counts to compensate + _deliveryAttempts = 0; + } + else + { + //the message(s) should be redelivered, adjust expected message + _expectedMessage -= _numMsgsToBeRedelivered; + } + _logger.debug("XXX _expectedMessage: " + _expectedMessage + " _deliveryAttempts : " + _deliveryAttempts + " _numMsgsToBeRedelivered=" + _numMsgsToBeRedelivered); + //reset count of messages expected to be redelivered + _numMsgsToBeRedelivered = 0; + } + else + { + //consume the message + switch(deliveryMode) + { + case Session.SESSION_TRANSACTED: + session.commit(); + break; + case Session.CLIENT_ACKNOWLEDGE: + message.acknowledge(); + break; + case Session.DUPS_OK_ACKNOWLEDGE: + //fall-through + case Session.AUTO_ACKNOWLEDGE: + //do nothing, onMessage will ack on exit. + break; + } + } + + if (msgId == MSG_COUNT) + { + //if this is the last message let the test complete. + if (expectedTotalNumberOfDeliveries == _totalNumDeliveries) + { + _awaitCompletion.countDown(); + } + else + { + failAsyncTest("Last message received, but we have not had the " + + "expected number of total delivieres. Received " + _totalNumDeliveries + " Expecting : " + expectedTotalNumberOfDeliveries); + } + } + } + catch (JMSException e) + { + failAsyncTest(e.getMessage()); + } + } + }); + } + + private void failAsyncTest(String msg) + { + _logger.error("Failing test because: " + msg); + _failMsg = msg; + _failed = true; + _awaitCompletion.countDown(); + } + + private void doSynchronousTest(final Session session, final MessageConsumer consumer, final int deliveryMode, final int maxDeliveryCount, + final int expectedTotalNumberOfDeliveries, final ArrayList<Integer> redeliverMsgs) throws JMSException, AMQException, InterruptedException + { + if(deliveryMode == Session.AUTO_ACKNOWLEDGE + || deliveryMode == Session.DUPS_OK_ACKNOWLEDGE + || deliveryMode == org.apache.qpid.jms.Session.PRE_ACKNOWLEDGE + || deliveryMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE) + { + fail("Max Delivery feature is not supported with this acknowledgement mode" + + "when using synchronous message delivery."); + } + + int _deliveryAttempts = 0; //number of times given message(s) have been seen + int _numMsgsToBeRedelivered = 0; //number of messages to rollback/recover + int _totalNumDeliveries = 0; + int _expectedMessage = 1; + + while(!_failed) + { + Message message = consumer.receive(1000); + + _totalNumDeliveries++; + + if (message == null) + { + fail("Should not get null messages"); + return; + } + + try + { + int msgId = message.getIntProperty("count"); + + _logger.info("Received message: " + msgId); + + //check the message is the one we expected + assertEquals("Unexpected message.", _expectedMessage, msgId); + + _expectedMessage++; + + //keep track of the overall deliveries to ensure we don't see more than expected + assertTrue("Exceeded expected total number of deliveries.", + _totalNumDeliveries <= expectedTotalNumberOfDeliveries ); + + //check if this message is one chosen to be rolled back / recovered + if(redeliverMsgs.contains(msgId)) + { + //keep track of the number of messages we will have redelivered + //upon rollback/recover + _numMsgsToBeRedelivered++; + + if(redeliverMsgs.contains(msgId +1)) + { + //next message is going to be rolled back / recovered too. + //skip ahead to it + continue; + } + + _deliveryAttempts++; //increment count of times the current rolled back/recovered message(s) have been seen + + switch(deliveryMode) + { + case Session.SESSION_TRANSACTED: + session.rollback(); + break; + case Session.CLIENT_ACKNOWLEDGE: + session.recover(); + + //sleep then do a synchronous op to give the broker + //time to resend all the messages + Thread.sleep(500); + ((AMQSession) session).sync(); + break; + } + + if( _deliveryAttempts >= maxDeliveryCount) + { + //the client should have rejected the latest messages upon then + //above recover/rollback, adjust counts to compensate + _deliveryAttempts = 0; + } + else + { + //the message(s) should be redelivered, adjust expected message + _expectedMessage -= _numMsgsToBeRedelivered; + } + + //As we just rolled back / recovered, we must reset the + //count of messages expected to be redelivered + _numMsgsToBeRedelivered = 0; + } + else + { + //consume the message + switch(deliveryMode) + { + case Session.SESSION_TRANSACTED: + session.commit(); + break; + case Session.CLIENT_ACKNOWLEDGE: + message.acknowledge(); + break; + } + } + + if (msgId == MSG_COUNT) + { + //if this is the last message let the test complete. + assertTrue("Last message received, but we have not had the " + + "expected number of total delivieres", + expectedTotalNumberOfDeliveries == _totalNumDeliveries); + + break; + } + } + catch (JMSException e) + { + fail(e.getMessage()); + } + } + } + + private boolean isDurSubTest() + { + return getTestQueueName().contains("DurableSubscription"); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/CloseAfterConnectionFailureTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/CloseAfterConnectionFailureTest.java deleted file mode 100644 index 91e681131f..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/CloseAfterConnectionFailureTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * - * 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.qpid.test.unit.client.connection; - -import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQConnection; -import org.apache.qpid.client.AMQConnectionURL; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.test.utils.QpidBrokerTestCase; -import org.apache.qpid.url.URLSyntaxException; - -import javax.jms.ExceptionListener; -import javax.jms.JMSException; -import javax.jms.MessageConsumer; -import javax.jms.Session; -import java.util.concurrent.CountDownLatch; - -public class CloseAfterConnectionFailureTest extends QpidBrokerTestCase implements ExceptionListener -{ - private int sessionCount = 0; - AMQConnection connection; - Session session; - MessageConsumer consumer; - private CountDownLatch _latch = new CountDownLatch(1); - private JMSException _fail; - - public void testNoFailover() throws URLSyntaxException, Exception, - InterruptedException, JMSException - { - //This test uses hard coded connection string so only runs on InVM case - if (!isExternalBroker()) - { - String connectionString = "amqp://guest:guest@/test?brokerlist='vm://:1?connectdelay='500',retries='3'',failover='nofailover'"; - - AMQConnectionURL url = new AMQConnectionURL(connectionString); - - try - { - //Start the connection so it will use the retries - connection = new AMQConnection(url); - - connection.setExceptionListener(this); - - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - consumer = session.createConsumer(session.createQueue(this.getName())); - - //Kill connection - stopBroker(); - - _latch.await(); - - if (_fail != null) - { - _fail.printStackTrace(System.out); - fail("Exception thrown:" + _fail.getMessage()); - } - } - catch (AMQException e) - { - fail(e.getMessage()); - } - } - } - - public void onException(JMSException e) - { - System.out.println("Connection isClosed after connection Falure?:" + connection.isClosed()); - try - { - consumer.close(); - } - catch (JMSException jmse) - { - System.out.println("Consumer close failed with:" + jmse.getMessage()); - _fail = jmse; - } - System.out.println("Connection isClosed after connection Falure?:" + connection.isClosed()); - try - { - //Note that if we actually do session.close() we will lock up as the session will never receive a frame - // from the - ((AMQSession) session).close(10); - } - catch (JMSException jmse) - { - System.out.println("Session close failed with:" + jmse.getMessage()); - _fail = jmse; - } - System.out.println("Connection isClosed after connection Falure?:" + connection.isClosed()); - - try - { - connection.close(); - } - catch (JMSException jmse) - { - System.out.println("Session close failed with:" + jmse.getMessage()); - _fail = jmse; - } - System.out.println("Connection isClosed after connection Falure?:" + connection.isClosed()); - - _latch.countDown(); - - } - -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java index 545081fb43..a313475b11 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionFactoryTest.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.test.unit.client.connection; import org.apache.qpid.client.AMQConnection; diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java index 9ea116ae53..d3d9cf2984 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -841,4 +841,147 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase e.printStackTrace(); } } + + /** + * Tests that a subscriber created on a same <i>session</i> as producer with + * no local true does not receive messages. + */ + public void testNoLocalOnSameSession() throws Exception + { + Connection connection = getConnection(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic(getTestQueueName()); + MessageProducer producer = session.createProducer(topic); + TopicSubscriber subscriber = null; + try + { + subscriber = session.createDurableSubscriber(topic, getTestName(), null, true); + connection.start(); + + producer.send(createNextMessage(session, 1)); + + Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNull("Unexpected message received", m); + } + finally + { + session.unsubscribe(getTestName()); + } + } + + + /** + * Tests that a subscriber created on a same <i>connection</i> but separate + * <i>sessionM</i> as producer with no local true does not receive messages. + */ + public void testNoLocalOnSameConnection() throws Exception + { + Connection connection = getConnection(); + + Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = consumerSession.createTopic(getTestQueueName()); + MessageProducer producer = producerSession.createProducer(topic); + + TopicSubscriber subscriber = null; + try + { + subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true); + connection.start(); + + producer.send(createNextMessage(producerSession, 1)); + + Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNull("Unexpected message received", m); + } + finally + { + consumerSession.unsubscribe(getTestName()); + } + } + + /** + * Tests that if no-local is in use, that the messages are delivered when + * the client reconnects. + * + * Currently fails on the Java Broker due to QPID-3605. + */ + public void testNoLocalMessagesNotDeliveredAfterReconnection() throws Exception + { + Connection connection = getConnection(); + + Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = consumerSession.createTopic(getTestQueueName()); + MessageProducer producer = producerSession.createProducer(topic); + + TopicSubscriber subscriber = null; + try + { + subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true); + connection.start(); + + producer.send(createNextMessage(producerSession, 1)); + + Message m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNull("Unexpected message received", m); + + connection.close(); + + connection = getConnection(); + + consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + subscriber = consumerSession.createDurableSubscriber(topic, getTestName(), null, true); + connection.start(); + m = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNull("Message should not be received on a new connection", m); + } + finally + { + consumerSession.unsubscribe(getTestName()); + } + } + + /** + * Tests that messages are delivered normally to a subscriber on a separate connection despite + * the use of durable subscriber with no-local on the first connection. + */ + public void testNoLocalSubscriberAndSubscriberOnSeparateConnection() throws Exception + { + Connection noLocalConnection = getConnection(); + Connection connection = getConnection(); + + String noLocalSubId1 = getTestName() + "subId1"; + String subId = getTestName() + "subId2"; + + Session noLocalSession = noLocalConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic noLocalTopic = noLocalSession.createTopic(getTestQueueName()); + + Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = consumerSession.createTopic(getTestQueueName()); + + TopicSubscriber noLocalSubscriber = null; + TopicSubscriber subscriber = null; + try + { + MessageProducer producer = noLocalSession.createProducer(noLocalTopic); + noLocalSubscriber = noLocalSession.createDurableSubscriber(noLocalTopic, noLocalSubId1, null, true); + subscriber = consumerSession.createDurableSubscriber(topic, subId, null, true); + noLocalConnection.start(); + connection.start(); + + producer.send(createNextMessage(noLocalSession, 1)); + + Message m1 = noLocalSubscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNull("Subscriber on nolocal connection should not receive message", m1); + + Message m2 = subscriber.receive(NEGATIVE_RECEIVE_TIMEOUT); + assertNotNull("Subscriber on non-nolocal connection should receive message", m2); + } + finally + { + noLocalSession.unsubscribe(noLocalSubId1); + consumerSession.unsubscribe(subId); + } + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java index bc2cbe714f..b8b5a29a43 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java @@ -22,10 +22,13 @@ package org.apache.qpid.test.unit.transacted; import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.configuration.ClientProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jms.*; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -36,17 +39,16 @@ import java.util.concurrent.TimeUnit; */ public class CommitRollbackTest extends QpidBrokerTestCase { - protected AMQConnection conn; - protected String queue = "direct://amq.direct//Qpid.Client.Transacted.CommitRollback.queue"; - protected static int testMethod = 0; - protected String payload = "xyzzy"; + private static final Logger _logger = LoggerFactory.getLogger(CommitRollbackTest.class); + private static final int POSIITIVE_TIMEOUT = 2000; + + protected AMQConnection _conn; private Session _session; private MessageProducer _publisher; private Session _pubSession; private MessageConsumer _consumer; - Queue _jmsQueue; + private Queue _jmsQueue; - private static final Logger _logger = LoggerFactory.getLogger(CommitRollbackTest.class); private boolean _gotone = false; private boolean _gottwo = false; private boolean _gottwoRedelivered = false; @@ -54,31 +56,24 @@ public class CommitRollbackTest extends QpidBrokerTestCase protected void setUp() throws Exception { super.setUp(); - testMethod++; - queue += testMethod; - newConnection(); } private void newConnection() throws Exception { - conn = (AMQConnection) getConnection("guest", "guest"); + _logger.debug("calling newConnection()"); + _conn = (AMQConnection) getConnection(); - _session = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE); + _session = _conn.createSession(true, Session.SESSION_TRANSACTED); - _jmsQueue = _session.createQueue(queue); + final String queueName = getTestQueueName(); + _jmsQueue = _session.createQueue(queueName); _consumer = _session.createConsumer(_jmsQueue); - _pubSession = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE); + _pubSession = _conn.createSession(true, Session.SESSION_TRANSACTED); - _publisher = _pubSession.createProducer(_pubSession.createQueue(queue)); + _publisher = _pubSession.createProducer(_pubSession.createQueue(queueName)); - conn.start(); - } - - protected void tearDown() throws Exception - { - conn.close(); - super.tearDown(); + _conn.start(); } /** @@ -88,6 +83,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testPutThenDisconnect() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -96,7 +93,7 @@ public class CommitRollbackTest extends QpidBrokerTestCase _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); _logger.info("reconnecting without commit"); - conn.close(); + _conn.close(); newConnection(); @@ -116,6 +113,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testPutThenCloseDisconnect() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -127,7 +126,7 @@ public class CommitRollbackTest extends QpidBrokerTestCase _publisher.close(); _logger.info("reconnecting without commit"); - conn.close(); + _conn.close(); newConnection(); @@ -148,6 +147,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testPutThenRollback() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -171,6 +172,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testGetThenDisconnect() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -186,7 +189,7 @@ public class CommitRollbackTest extends QpidBrokerTestCase assertNotNull("retrieved message is null", msg); _logger.info("closing connection"); - conn.close(); + _conn.close(); newConnection(); @@ -207,6 +210,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testGetThenCloseDisconnect() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -224,7 +229,7 @@ public class CommitRollbackTest extends QpidBrokerTestCase _logger.info("reconnecting without commit"); _consumer.close(); - conn.close(); + _conn.close(); newConnection(); @@ -245,6 +250,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testGetThenRollback() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -283,6 +290,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testGetThenCloseRollback() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -324,6 +333,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testSend2ThenRollback() throws Exception { + newConnection(); + int run = 0; while (run < 10) { @@ -424,6 +435,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase */ public void testSend2ThenCloseAfter1andTryAgain() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -470,6 +483,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase public void testPutThenRollbackThenGet() throws Exception { + newConnection(); + assertTrue("session is not transacted", _session.getTransacted()); assertTrue("session is not transacted", _pubSession.getTransacted()); @@ -501,13 +516,15 @@ public class CommitRollbackTest extends QpidBrokerTestCase /** * Qpid-1163 - * Check that when commt is called inside onMessage then + * Check that when commit is called inside onMessage then * the last message is nor redelivered. * * @throws Exception */ - public void testCommitWhithinOnMessage() throws Exception + public void testCommitWithinOnMessage() throws Exception { + newConnection(); + Queue queue = (Queue) getInitialContext().lookup("queue"); // create a consumer MessageConsumer cons = _session.createConsumer(queue); @@ -518,8 +535,8 @@ public class CommitRollbackTest extends QpidBrokerTestCase _session.commit(); _logger.info("Sent message to queue"); CountDownLatch cd = new CountDownLatch(1); - cons.setMessageListener(new CommitWhithinOnMessageListener(cd)); - conn.start(); + cons.setMessageListener(new CommitWithinOnMessageListener(cd)); + _conn.start(); cd.await(30, TimeUnit.SECONDS); if( cd.getCount() > 0 ) { @@ -527,10 +544,10 @@ public class CommitRollbackTest extends QpidBrokerTestCase } // Check that the message has been dequeued _session.close(); - conn.close(); - conn = (AMQConnection) getConnection("guest", "guest"); - conn.start(); - Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE); + _conn.close(); + _conn = (AMQConnection) getConnection(); + _conn.start(); + Session session = _conn.createSession(false, Session.CLIENT_ACKNOWLEDGE); cons = session.createConsumer(queue); message = cons.receiveNoWait(); if(message != null) @@ -546,10 +563,55 @@ public class CommitRollbackTest extends QpidBrokerTestCase } } - private class CommitWhithinOnMessageListener implements MessageListener + /** + * This test ensures that after exhausting credit (prefetch), a {@link Session#rollback()} successfully + * restores credit and allows the same messages to be re-received. + */ + public void testRollbackSessionAfterCreditExhausted() throws Exception + { + final int maxPrefetch= 5; + + // We send more messages than prefetch size. This ensure that if the 0-10 client were to + // complete the message commands before the rollback command is sent, the broker would + // send additional messages utilising the release credit. This problem would manifest itself + // as an incorrect message (or no message at all) being received at the end of the test. + + final int numMessages = maxPrefetch * 2; + + setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, String.valueOf(maxPrefetch)); + + newConnection(); + + assertEquals("Prefetch not reset", maxPrefetch, ((AMQSession<?, ?>)_session).getDefaultPrefetch()); + + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + sendMessage(_pubSession, _publisher.getDestination(), numMessages); + _pubSession.commit(); + + for (int i=0 ;i< maxPrefetch; i++) + { + final Message message = _consumer.receive(POSIITIVE_TIMEOUT); + assertNotNull("Received:" + i, message); + assertEquals("Unexpected message received", i, message.getIntProperty(INDEX)); + } + + _logger.info("Rolling back"); + _session.rollback(); + + _logger.info("Receiving messages"); + + Message result = _consumer.receive(POSIITIVE_TIMEOUT);; + assertNotNull("Message expected", result); + // Expect the first message + assertEquals("Unexpected message received", 0, result.getIntProperty(INDEX)); + } + + private class CommitWithinOnMessageListener implements MessageListener { private CountDownLatch _cd; - private CommitWhithinOnMessageListener(CountDownLatch cd) + private CommitWithinOnMessageListener(CountDownLatch cd) { _cd = cd; } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactedTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactedTest.java index 045deab052..653ab8f733 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactedTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactedTest.java @@ -135,197 +135,172 @@ public class TransactedTest extends QpidBrokerTestCase public void testCommit() throws Exception { - try - { -// add some messages - _logger.info("Send prep A"); - prepProducer1.send(prepSession.createTextMessage("A")); - _logger.info("Send prep B"); - prepProducer1.send(prepSession.createTextMessage("B")); - _logger.info("Send prep C"); - prepProducer1.send(prepSession.createTextMessage("C")); - - // send and receive some messages - _logger.info("Send X to Q2"); - producer2.send(session.createTextMessage("X")); - _logger.info("Send Y to Q2"); - producer2.send(session.createTextMessage("Y")); - _logger.info("Send Z to Q2"); - producer2.send(session.createTextMessage("Z")); - - _logger.info("Read A from Q1"); - expect("A", consumer1.receive(1000)); - _logger.info("Read B from Q1"); - expect("B", consumer1.receive(1000)); - _logger.info("Read C from Q1"); - expect("C", consumer1.receive(1000)); - - // commit - _logger.info("session commit"); - session.commit(); - _logger.info("Start test Connection"); - testCon.start(); - - // ensure sent messages can be received and received messages are gone - _logger.info("Read X from Q2"); - expect("X", testConsumer2.receive(1000)); - _logger.info("Read Y from Q2"); - expect("Y", testConsumer2.receive(1000)); - _logger.info("Read Z from Q2"); - expect("Z", testConsumer2.receive(1000)); - - _logger.info("create test session on Q1"); - testConsumer1 = testSession.createConsumer(queue1); - _logger.info("Read null from Q1"); - assertTrue(null == testConsumer1.receive(1000)); - _logger.info("Read null from Q2"); - assertTrue(null == testConsumer2.receive(1000)); - } - catch (Throwable e) - { - e.printStackTrace(); - fail(e.getMessage()); - } + _logger.info("Send prep A"); + prepProducer1.send(prepSession.createTextMessage("A")); + _logger.info("Send prep B"); + prepProducer1.send(prepSession.createTextMessage("B")); + _logger.info("Send prep C"); + prepProducer1.send(prepSession.createTextMessage("C")); + + // send and receive some messages + _logger.info("Send X to Q2"); + producer2.send(session.createTextMessage("X")); + _logger.info("Send Y to Q2"); + producer2.send(session.createTextMessage("Y")); + _logger.info("Send Z to Q2"); + producer2.send(session.createTextMessage("Z")); + + _logger.info("Read A from Q1"); + expect("A", consumer1.receive(1000)); + _logger.info("Read B from Q1"); + expect("B", consumer1.receive(1000)); + _logger.info("Read C from Q1"); + expect("C", consumer1.receive(1000)); + + // commit + _logger.info("session commit"); + session.commit(); + _logger.info("Start test Connection"); + testCon.start(); + + // ensure sent messages can be received and received messages are gone + _logger.info("Read X from Q2"); + expect("X", testConsumer2.receive(1000)); + _logger.info("Read Y from Q2"); + expect("Y", testConsumer2.receive(1000)); + _logger.info("Read Z from Q2"); + expect("Z", testConsumer2.receive(1000)); + + _logger.info("create test session on Q1"); + testConsumer1 = testSession.createConsumer(queue1); + _logger.info("Read null from Q1"); + assertTrue(null == testConsumer1.receive(1000)); + _logger.info("Read null from Q2"); + assertTrue(null == testConsumer2.receive(1000)); } public void testRollback() throws Exception { - try - { -// add some messages - _logger.info("Send prep RB_A"); - prepProducer1.send(prepSession.createTextMessage("RB_A")); - _logger.info("Send prep RB_B"); - prepProducer1.send(prepSession.createTextMessage("RB_B")); - _logger.info("Send prep RB_C"); - prepProducer1.send(prepSession.createTextMessage("RB_C")); - - _logger.info("Sending RB_X RB_Y RB_Z"); - producer2.send(session.createTextMessage("RB_X")); - producer2.send(session.createTextMessage("RB_Y")); - producer2.send(session.createTextMessage("RB_Z")); - _logger.info("Receiving RB_A RB_B"); - expect("RB_A", consumer1.receive(1000)); - expect("RB_B", consumer1.receive(1000)); - // Don't consume 'RB_C' leave it in the prefetch cache to ensure rollback removes it. - // Quick sleep to ensure 'RB_C' gets pre-fetched - Thread.sleep(500); - - // rollback - _logger.info("rollback"); - session.rollback(); - - _logger.info("Receiving RB_A RB_B RB_C"); - // ensure sent messages are not visible and received messages are requeued - expect("RB_A", consumer1.receive(1000), true); - expect("RB_B", consumer1.receive(1000), true); - expect("RB_C", consumer1.receive(1000), true); - _logger.info("Starting new connection"); - testCon.start(); - testConsumer1 = testSession.createConsumer(queue1); - _logger.info("Testing we have no messages left"); - assertTrue(null == testConsumer1.receive(1000)); - assertTrue(null == testConsumer2.receive(1000)); - - session.commit(); - - _logger.info("Testing we have no messages left after commit"); - assertTrue(null == testConsumer1.receive(1000)); - assertTrue(null == testConsumer2.receive(1000)); - } - catch (Throwable e) - { - e.printStackTrace(); - fail(e.getMessage()); - } + // add some messages + _logger.info("Send prep RB_A"); + prepProducer1.send(prepSession.createTextMessage("RB_A")); + _logger.info("Send prep RB_B"); + prepProducer1.send(prepSession.createTextMessage("RB_B")); + _logger.info("Send prep RB_C"); + prepProducer1.send(prepSession.createTextMessage("RB_C")); + + _logger.info("Sending RB_X RB_Y RB_Z"); + producer2.send(session.createTextMessage("RB_X")); + producer2.send(session.createTextMessage("RB_Y")); + producer2.send(session.createTextMessage("RB_Z")); + _logger.info("Receiving RB_A RB_B"); + expect("RB_A", consumer1.receive(1000)); + expect("RB_B", consumer1.receive(1000)); + // Don't consume 'RB_C' leave it in the prefetch cache to ensure rollback removes it. + // Quick sleep to ensure 'RB_C' gets pre-fetched + Thread.sleep(500); + + // rollback + _logger.info("rollback"); + session.rollback(); + + _logger.info("Receiving RB_A RB_B RB_C"); + // ensure sent messages are not visible and received messages are requeued + expect("RB_A", consumer1.receive(1000), true); + expect("RB_B", consumer1.receive(1000), true); + expect("RB_C", consumer1.receive(1000), isBroker010()?false:true); + _logger.info("Starting new connection"); + testCon.start(); + testConsumer1 = testSession.createConsumer(queue1); + _logger.info("Testing we have no messages left"); + assertTrue(null == testConsumer1.receive(1000)); + assertTrue(null == testConsumer2.receive(1000)); + + session.commit(); + + _logger.info("Testing we have no messages left after commit"); + assertTrue(null == testConsumer1.receive(1000)); + assertTrue(null == testConsumer2.receive(1000)); } public void testResendsMsgsAfterSessionClose() throws Exception { - try - { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con = (AMQConnection) getConnection("guest", "guest"); - Session consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); - AMQQueue queue3 = new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), false); - MessageConsumer consumer = consumerSession.createConsumer(queue3); + Session consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); + AMQQueue queue3 = new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), false); + MessageConsumer consumer = consumerSession.createConsumer(queue3); - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); - Session producerSession = con2.createSession(true, Session.SESSION_TRANSACTED); - MessageProducer producer = producerSession.createProducer(queue3); + AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); + Session producerSession = con2.createSession(true, Session.SESSION_TRANSACTED); + MessageProducer producer = producerSession.createProducer(queue3); - _logger.info("Sending four messages"); - producer.send(producerSession.createTextMessage("msg1")); - producer.send(producerSession.createTextMessage("msg2")); - producer.send(producerSession.createTextMessage("msg3")); - producer.send(producerSession.createTextMessage("msg4")); + _logger.info("Sending four messages"); + producer.send(producerSession.createTextMessage("msg1")); + producer.send(producerSession.createTextMessage("msg2")); + producer.send(producerSession.createTextMessage("msg3")); + producer.send(producerSession.createTextMessage("msg4")); - producerSession.commit(); + producerSession.commit(); - _logger.info("Starting connection"); - con.start(); - TextMessage tm = (TextMessage) consumer.receive(); - assertNotNull(tm); - assertEquals("msg1", tm.getText()); + _logger.info("Starting connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(); + assertNotNull(tm); + assertEquals("msg1", tm.getText()); - consumerSession.commit(); + consumerSession.commit(); - _logger.info("Received and committed first message"); - tm = (TextMessage) consumer.receive(1000); - assertNotNull(tm); - assertEquals("msg2", tm.getText()); + _logger.info("Received and committed first message"); + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg2", tm.getText()); - tm = (TextMessage) consumer.receive(1000); - assertNotNull(tm); - assertEquals("msg3", tm.getText()); + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg3", tm.getText()); - tm = (TextMessage) consumer.receive(1000); - assertNotNull(tm); - assertEquals("msg4", tm.getText()); + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg4", tm.getText()); - _logger.info("Received all four messages. Closing connection with three outstanding messages"); + _logger.info("Received all four messages. Closing connection with three outstanding messages"); - consumerSession.close(); + consumerSession.close(); - consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); + consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); - consumer = consumerSession.createConsumer(queue3); + consumer = consumerSession.createConsumer(queue3); - // no ack for last three messages so when I call recover I expect to get three messages back - tm = (TextMessage) consumer.receive(3000); - assertNotNull(tm); - assertEquals("msg2", tm.getText()); - assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + // no ack for last three messages so when I call recover I expect to get three messages back + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg2", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); - tm = (TextMessage) consumer.receive(3000); - assertNotNull(tm); - assertEquals("msg3", tm.getText()); - assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg3", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); - tm = (TextMessage) consumer.receive(3000); - assertNotNull(tm); - assertEquals("msg4", tm.getText()); - assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg4", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); - _logger.info("Received redelivery of three messages. Committing"); + _logger.info("Received redelivery of three messages. Committing"); - consumerSession.commit(); + consumerSession.commit(); - _logger.info("Called commit"); + _logger.info("Called commit"); - tm = (TextMessage) consumer.receive(1000); - assertNull(tm); + tm = (TextMessage) consumer.receive(1000); + assertNull(tm); - _logger.info("No messages redelivered as is expected"); + _logger.info("No messages redelivered as is expected"); - con.close(); - con2.close(); - } - catch (Throwable e) - { - e.printStackTrace(); - fail(e.getMessage()); - } + con.close(); + con2.close(); } private void expect(String text, Message msg) throws JMSException diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java index 1fde6c7c73..3a1710671c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -51,18 +51,26 @@ import org.apache.qpid.management.common.mbeans.UserManagement; */ public class JMXTestUtils { - QpidBrokerTestCase _test; - MBeanServerConnection _mbsc; - JMXConnector _jmxc; + private static final String DEFAULT_PASSWORD = "admin"; + private static final String DEFAULT_USERID = "admin"; - private String USER; - private String PASSWORD; + private MBeanServerConnection _mbsc; + private JMXConnector _jmxc; + + private final String _user; + private final String _password; + private final QpidBrokerTestCase _test; public JMXTestUtils(QpidBrokerTestCase test, String user, String password) { _test = test; - USER = user; - PASSWORD = password; + _user = user; + _password = password; + } + + public JMXTestUtils(QpidBrokerTestCase test) + { + this(test, DEFAULT_USERID, DEFAULT_PASSWORD); } public void setUp() throws IOException, ConfigurationException, Exception @@ -73,7 +81,7 @@ public class JMXTestUtils public void open() throws Exception { _jmxc = JMXConnnectionFactory.getJMXConnection(5000, "127.0.0.1", - _test.getManagementPort(_test.getPort()), USER, PASSWORD); + _test.getManagementPort(_test.getPort()), _user, _password); _mbsc = _jmxc.getMBeanServerConnection(); } @@ -319,6 +327,12 @@ public class JMXTestUtils return getManagedObject(managedClass, objectName); } + public boolean isManagedObjectExist(String query) + { + return !queryObjects(query).isEmpty(); + + } + public <T> T getManagedObject(Class<T> managedClass, ObjectName objectName) { return MBeanServerInvocationHandler.newProxyInstance(_mbsc, objectName, managedClass, false); @@ -370,7 +384,7 @@ public class JMXTestUtils } /** - * Retrive {@link ServerInformation} JMX MBean. + * Retrieve {@link ServerInformation} JMX MBean. */ public ServerInformation getServerInformation() { @@ -387,7 +401,7 @@ public class JMXTestUtils } /** - * Retrive all {@link ManagedConnection} objects. + * Retrieve all {@link ManagedConnection} objects. */ public List<ManagedConnection> getAllManagedConnections() { @@ -402,7 +416,7 @@ public class JMXTestUtils } /** - * Retrive all {@link ManagedConnection} objects for a particular virtual host. + * Retrieve all {@link ManagedConnection} objects for a particular virtual host. */ public List<ManagedConnection> getManagedConnections(String vhost) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java index bb44aea659..f680a20288 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java @@ -823,7 +823,7 @@ public class QpidBrokerTestCase extends QpidTestCase * @throws ConfigurationException when loading the current config file * @throws IOException when writing the new config file */ - protected void setConfigurationProperty(String property, String value) + public void setConfigurationProperty(String property, String value) throws ConfigurationException, IOException { // Choose which file to write the property to based on prefix. @@ -945,7 +945,7 @@ public class QpidBrokerTestCase extends QpidTestCase protected boolean isJavaBroker() { - return _brokerLanguage.equals("java") || _brokerType.equals("vm"); + return _brokerLanguage.equals("java"); } protected boolean isCppBroker() @@ -955,7 +955,7 @@ public class QpidBrokerTestCase extends QpidTestCase protected boolean isExternalBroker() { - return !_brokerType.equals("vm"); //TODO + return !isInternalBroker(); } protected boolean isInternalBroker() @@ -1326,7 +1326,7 @@ public class QpidBrokerTestCase extends QpidTestCase */ public void reloadBrokerSecurityConfig() throws Exception { - JMXTestUtils jmxu = new JMXTestUtils(this, "admin" , "admin"); + JMXTestUtils jmxu = new JMXTestUtils(this); jmxu.open(); try diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes index 39b4d542db..66a20bcfc1 100755 --- a/qpid/java/test-profiles/CPPExcludes +++ b/qpid/java/test-profiles/CPPExcludes @@ -20,6 +20,9 @@ // QPID-3391: the C++ broker does not currently validate the exchange creation arguments org.apache.qpid.test.client.destination.AddressBasedDestinationTest#testCreateExchangeWithNonsenseArgs +// QPID-3576: Java client issue. MessageConsumer#close() time-out. +org.apache.qpid.test.client.destination.AddressBasedDestinationTest#testDeleteOptions + //This test requires SSL, but SSL is only enabled for the C++ broker in the cpp.ssl test profile //which runs *all* the tests with SSL, so this one can be excluded safely enough org.apache.qpid.test.unit.client.AMQSSLConnectionTest#* @@ -27,9 +30,6 @@ org.apache.qpid.test.unit.client.AMQSSLConnectionTest#* org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* org.apache.qpid.client.ResetMessageListenerTest#* -// This test is not finished -org.apache.qpid.test.client.failover.FailoverTest#test4MinuteFailover - // Those tests are testing 0.8 specific semantics org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P @@ -54,6 +54,12 @@ org.apache.qpid.server.queue.AMQPriorityQueueTest#* // the 0-10 c++ broker does not implement the extended LVQ semantics which the Java Broker does org.apache.qpid.server.queue.ConflationQueueTest#* +// the 0-10 c++ broker does not implement sorted queues +org.apache.qpid.server.queue.SortedQueueTest#* + +// the 0-10 c++ broker does not implement DLQ +org.apache.qpid.test.unit.client.MaxDeliveryCountTest#* + //this test checks explicitly for 0-8 flow control semantics org.apache.qpid.test.client.FlowControlTest#* @@ -83,9 +89,6 @@ org.apache.qpid.test.client.QueueBrowsingFlowToDiskTest#* // This test currently does not pick up the runtime location of the nonVm queueBacking store. org.apache.qpid.test.unit.close.FlowToDiskBackingQueueDeleteTest#* -// This test may use QpidTestCase but it is not using the getConnection and is hardwired to InVM -org.apache.qpid.test.unit.client.connection.CloseAfterConnectionFailureTest#* - //QPID-1818 : 0-10 Client code path does not correctly restore a transacted session after failover. org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* @@ -134,13 +137,6 @@ org.apache.qpid.test.client.message.SelectorTest#testRuntimeSelectorError //QPID-942 : Implemented Channel.Flow based Producer Side flow control to the Java Broker (not in CPP Broker) org.apache.qpid.server.queue.ProducerFlowControlTest#* -//QPID-1950 : Commit to test this failure. This is a MINA only failure so it cannot be tested when using 010. -org.apache.qpid.server.failover.MessageDisappearWithIOExceptionTest#* - -// These are recent test additions that are failing with the c++ broker -// Temporarily disabling until properly investigated. -org.apache.qpid.test.unit.ack.FailoverBeforeConsumingRecoverTest#* - org.apache.qpid.test.client.RollbackOrderTest#testOrderingAfterRollbackOnMessage#* //Excluded due to QPID-1447 : CPP broker does not have SlowConsumer Disconnection @@ -176,14 +172,3 @@ org.apache.qpid.server.management.AMQUserManagementMBeanTest#* // QPID-3133: On 0-10, the exception listener is currently not invoked when reconnection fails to occurs. org.apache.qpid.server.failover.FailoverMethodTest#* -//QPID-3468: exclude QueueBrowser related failover tests -org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverAsQueueBrowserCreated diff --git a/qpid/java/test-profiles/CPPNoPrefetchExcludes b/qpid/java/test-profiles/CPPNoPrefetchExcludes index ebcd430161..e10b2fa659 100644 --- a/qpid/java/test-profiles/CPPNoPrefetchExcludes +++ b/qpid/java/test-profiles/CPPNoPrefetchExcludes @@ -19,13 +19,6 @@ org.apache.qpid.test.unit.transacted.TransactedTest#testRollback -org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverWithQueueBrowser - org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testDurabilityNOACK org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testDurabilityNOACKSessionPerConnection org.apache.qpid.test.unit.xa.TopicTest#testMigrateDurableSubscriber diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index b1edd07f87..8cad91ef2d 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -29,18 +29,6 @@ org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClo // QPID-3424 : Test fails to start external broker due to Derby Exception. org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* -// QPID-1816 : Client Ack has not been addressed -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testDirtyClientAck -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#testClientAck -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverTest#testDirtyClientAck -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverTest#testClientAck - - -// QPID-143 : Failover can occur between receive and ack but we don't stop the ack. -// Just fully disable both tests as they are highlighting to many Java Client race conditions -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverOnMessageTest#* -org.apache.qpid.test.unit.ack.AcknowledgeAfterFailoverTest#* - // QPID-2418 : The queue backing the dur sub is not currently deleted at subscription change, so the test will fail. org.apache.qpid.test.unit.ct.DurableSubscriberTest#testResubscribeWithChangedSelectorAndRestart diff --git a/qpid/java/test-profiles/Java010Excludes b/qpid/java/test-profiles/Java010Excludes index e7718b982d..59cb5066f1 100755 --- a/qpid/java/test-profiles/Java010Excludes +++ b/qpid/java/test-profiles/Java010Excludes @@ -42,19 +42,12 @@ org.apache.qpid.server.logging.ChannelLoggingTest#testChannelStartsFlowStopped org.apache.qpid.server.logging.ChannelLoggingTest#testChannelStartConsumerFlowStarted org.apache.qpid.server.logging.SubscriptionLoggingTest#testSubscriptionSuspend -// 0-10 Broker does not have a JMX connection MBean -org.apache.qpid.management.jmx.ManagementActorLoggingTest#testConnectionCloseViaManagement -org.apache.qpid.management.jmx.MessageConnectionStatisticsTest#* - // 0-10 is not supported by the MethodRegistry org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* //QPID-942 : Implemented Channel.Flow based Producer Side flow control to the Java Broker (not in CPP Broker) org.apache.qpid.server.queue.ProducerFlowControlTest#* -//QPID-1950 : Commit to test this failure. This is a MINA only failure so it cannot be tested when using 010. -org.apache.qpid.server.failover.MessageDisappearWithIOExceptionTest#* - //QPID-1864: rollback with subscriptions does not work in 0-10 yet org.apache.qpid.test.client.RollbackOrderTest#testOrderingAfterRollbackOnMessage @@ -71,22 +64,7 @@ org.apache.qpid.server.failover.FailoverMethodTest#* org.apache.qpid.test.client.destination.AddressBasedDestinationTest#testCreateExchangeWithArgs org.apache.qpid.test.client.destination.AddressBasedDestinationTest#testSessionCreateTopicWithExchangeArgs -// QPID-1935: the following tests are failing on 0.10 profiles -org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverWithQueueBrowser - // QPID-3432: These tests test the behaviour of 0-8..-0-9-1 specific system property (amqj.default_syncwrite_timeout) org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* -//QPID-3468: exclude QueueBrowser related failover tests -org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverAsQueueBrowserCreated -org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser -org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverAsQueueBrowserCreated diff --git a/qpid/java/test-profiles/JavaExcludes b/qpid/java/test-profiles/JavaExcludes index 8de36cbd9a..08e16ff216 100644 --- a/qpid/java/test-profiles/JavaExcludes +++ b/qpid/java/test-profiles/JavaExcludes @@ -28,13 +28,6 @@ org.apache.qpid.test.client.queue.QueuePolicyTest#testRejectPolicy //Moved from JavaStandaloneExcludes when it was removed /////////////////////////////////////////////////////// -// QPID-3426: The following test is broken. -// This is a long running test so should exclude from normal runs -org.apache.qpid.test.client.failover.FailoverTest#test4MinuteFailover - -// This test may use QpidTestCase but it is not using the getConnection and is hardwired to InVM -org.apache.qpid.test.unit.client.connection.CloseAfterConnectionFailureTest#* - //QPID-1818, QPID-1821 : Client code path does not correctly restore a transacted session after failover. org.apache.qpid.server.persistent.NoLocalAfterRecoveryTest#* @@ -43,3 +36,6 @@ org.apache.qpid.jms.xa.XAResourceTest#* //The Java broker doesnt support client auth org.apache.qpid.client.ssl.SSLTest#testMultipleCertsInSingleStore + +//QPID-3605 Durable subscriber with no-local true receives messages on re-connection +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testNoLocalMessagesNotDeliveredAfterReconnection diff --git a/qpid/java/test-profiles/JavaPre010Excludes b/qpid/java/test-profiles/JavaPre010Excludes index 68feaf1e2b..057d7c2c44 100644 --- a/qpid/java/test-profiles/JavaPre010Excludes +++ b/qpid/java/test-profiles/JavaPre010Excludes @@ -33,6 +33,7 @@ org.apache.qpid.test.client.destination.AddressBasedDestinationTest#* org.apache.qpid.test.client.queue.QueuePolicyTest#testRingPolicy org.apache.qpid.test.client.queue.QueuePolicyTest#testRejectPolicy org.apache.qpid.test.unit.message.JMSPropertiesTest#testApplicationProperties +org.apache.qpid.server.queue.AddressBasedSortedQueueTest#* // Those tests are written against the 0.10 path org.apache.qpid.test.unit.message.UTF8Test#* diff --git a/qpid/java/test-profiles/python_tests/Java010PythonExcludes b/qpid/java/test-profiles/python_tests/Java010PythonExcludes index 10e6298634..069003b0b8 100644 --- a/qpid/java/test-profiles/python_tests/Java010PythonExcludes +++ b/qpid/java/test-profiles/python_tests/Java010PythonExcludes @@ -66,19 +66,8 @@ qpid.tests.messaging.endpoints.SessionTests.testDoubleCommit qpid_tests.broker_0_10.message.MessageTests.test_credit_flow_bytes qpid_tests.broker_0_10.message.MessageTests.test_window_flow_bytes -#QPID-3592 Fails to receive more messages after restart -qpid_tests.broker_0_10.message.MessageTests.test_window_stop - -#QPID-3539 Tests fail because is incorrectly being done per session and not connection -qpid_tests.broker_0_10.message.MessageTests.test_no_local +#QPID-3605 Durable subscriber with no-local true receives messages on re-connection qpid_tests.broker_0_10.message.MessageTests.test_no_local_awkward -qpid_tests.broker_0_10.message.MessageTests.test_no_local_exclusive_subscribe - -#QPID-3593 Priority Queue test failures -qpid_tests.broker_0_10.priority.PriorityTests.test_browsing -qpid_tests.broker_0_10.priority.PriorityTests.test_prioritised_delivery_1 -qpid_tests.broker_0_10.priority.PriorityTests.test_prioritised_delivery_2 -qpid_tests.broker_0_10.priority.PriorityTests.test_requeue #QPID-3594 exclusive queues problem qpid_tests.broker_0_10.queue.QueueTests.test_declare_exclusive diff --git a/qpid/java/test-profiles/test_resources/ssl/java_broker_keystore.jks b/qpid/java/test-profiles/test_resources/ssl/java_broker_keystore.jks Binary files differindex 4c4449e20d..86670fa34b 100644 --- a/qpid/java/test-profiles/test_resources/ssl/java_broker_keystore.jks +++ b/qpid/java/test-profiles/test_resources/ssl/java_broker_keystore.jks diff --git a/qpid/java/tools/etc/perf-report.gnu b/qpid/java/tools/etc/perf-report.gnu index 6d5020efb5..b7662b0bfe 100644 --- a/qpid/java/tools/etc/perf-report.gnu +++ b/qpid/java/tools/etc/perf-report.gnu @@ -1,3 +1,22 @@ +# +# 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. +# + set terminal png set datafile separator "," diff --git a/qpid/java/tools/src/main/java/org/apache/qpid/tools/Clock.java b/qpid/java/tools/src/main/java/org/apache/qpid/tools/Clock.java index 37369959a8..979d2ef76f 100644 --- a/qpid/java/tools/src/main/java/org/apache/qpid/tools/Clock.java +++ b/qpid/java/tools/src/main/java/org/apache/qpid/tools/Clock.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.tools; /** diff --git a/qpid/java/tools/src/main/java/org/apache/qpid/tools/PerfTestController.java b/qpid/java/tools/src/main/java/org/apache/qpid/tools/PerfTestController.java index 5c98c645f4..5fca1fa4bd 100644 --- a/qpid/java/tools/src/main/java/org/apache/qpid/tools/PerfTestController.java +++ b/qpid/java/tools/src/main/java/org/apache/qpid/tools/PerfTestController.java @@ -1,3 +1,23 @@ +/* + * + * 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.qpid.tools; import java.io.FileWriter; |