From b2369f9be92c573bd37b3ccc3142b9900625434f Mon Sep 17 00:00:00 2001 From: Justin Ross Date: Thu, 20 Mar 2014 17:29:51 +0000 Subject: QPID-5600: Advance the version number on trunk to 0.29 git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579690 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/broker-core/src/main/java/broker.bnd | 2 +- qpid/java/client/src/main/java/client.bnd | 2 +- qpid/java/common.xml | 4 ++-- qpid/java/common/src/main/java/common.bnd | 2 +- qpid/java/management/common/src/main/java/management-common.bnd | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/broker.bnd b/qpid/java/broker-core/src/main/java/broker.bnd index 1a371a5efd..8b3f663ec6 100755 --- a/qpid/java/broker-core/src/main/java/broker.bnd +++ b/qpid/java/broker-core/src/main/java/broker.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-broker Bundle-Version: ${ver} diff --git a/qpid/java/client/src/main/java/client.bnd b/qpid/java/client/src/main/java/client.bnd index 63b1e3b34c..c2abcd55b5 100755 --- a/qpid/java/client/src/main/java/client.bnd +++ b/qpid/java/client/src/main/java/client.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-client Bundle-Version: ${ver} diff --git a/qpid/java/common.xml b/qpid/java/common.xml index 889cf3c4d8..87b8f2d175 100644 --- a/qpid/java/common.xml +++ b/qpid/java/common.xml @@ -24,9 +24,9 @@ - + - + diff --git a/qpid/java/common/src/main/java/common.bnd b/qpid/java/common/src/main/java/common.bnd index 76ee9d740f..498562f3c1 100755 --- a/qpid/java/common/src/main/java/common.bnd +++ b/qpid/java/common/src/main/java/common.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-common Bundle-Version: ${ver} diff --git a/qpid/java/management/common/src/main/java/management-common.bnd b/qpid/java/management/common/src/main/java/management-common.bnd index 459c3e60a8..cb080c9680 100644 --- a/qpid/java/management/common/src/main/java/management-common.bnd +++ b/qpid/java/management/common/src/main/java/management-common.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-management-common Bundle-Version: ${ver} -- cgit v1.2.1 From 68818478d8c394099456628ad2a930906413c458 Mon Sep 17 00:00:00 2001 From: Alex Rudyy Date: Fri, 21 Mar 2014 09:33:29 +0000 Subject: QPID-5635: Fix 'partitionCount' attribute setting for BoneCP connection pool UI in Web Management console UI git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579904 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/resources/virtualhost/store/pool/bonecp/add.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html b/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html index 0a83bd9f6f..5b053849a3 100644 --- a/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html +++ b/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html @@ -20,7 +20,7 @@ Partition Count: + name="partitionCount" value="4" smallDelta="1" constraints="{min:1,max:1000,places:0}"/> -- cgit v1.2.1 From cdfa97831a3653390fb11baf69d8015af84311bb Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Fri, 21 Mar 2014 11:18:45 +0000 Subject: QPID-5625 : Fix saving of "createdBy" attribute which is incorrectly saving as an object rather than a string git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579926 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java index ec48aa7936..1bdb638765 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java @@ -212,7 +212,7 @@ public abstract class AbstractConfiguredObject> im final AuthenticatedPrincipal currentUser = SecurityManager.getCurrentUser(); if(currentUser != null) { - _attributes.put(CREATED_BY, currentUser); + _attributes.put(CREATED_BY, currentUser.getName()); } } if(!_attributes.containsKey(CREATED_TIME)) -- cgit v1.2.1 From f34e698b3beb0aa5810adff8bc101b37aa6eb2d2 Mon Sep 17 00:00:00 2001 From: Alex Rudyy Date: Fri, 21 Mar 2014 12:44:50 +0000 Subject: QPID-5048: dby profiles, bdb test deps and config for perftests-systests & bdbstore-systests and a fix for clean between test runs Patch supplied by Andrew MacBean git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579935 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/bdbstore/pom.xml | 14 +++ qpid/java/bdbstore/systests/pom.xml | 61 +++++++++++ .../main/java/org/apache/qpid/server/Broker.java | 19 +++- qpid/java/pom.xml | 111 +++++++++++++++++---- qpid/java/qpid-perftests-systests/pom.xml | 14 +++ 5 files changed, 195 insertions(+), 24 deletions(-) (limited to 'qpid/java') diff --git a/qpid/java/bdbstore/pom.xml b/qpid/java/bdbstore/pom.xml index d7d12f0b33..5d34559a46 100644 --- a/qpid/java/bdbstore/pom.xml +++ b/qpid/java/bdbstore/pom.xml @@ -102,6 +102,20 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + diff --git a/qpid/java/bdbstore/systests/pom.xml b/qpid/java/bdbstore/systests/pom.xml index e8620d3426..fe718f9dac 100644 --- a/qpid/java/bdbstore/systests/pom.xml +++ b/qpid/java/bdbstore/systests/pom.xml @@ -33,6 +33,7 @@ ${project.basedir}${file.separator}..${file.separator}..${file.separator}test-profiles${file.separator}log4j-test.xml ${basedir}/../.. ${basedir}/../.. + ${basedir}/../../systests @@ -65,6 +66,66 @@ com.sleepycat je + + + org.apache.qpid + qpid-bdbstore + ${project.version} + test-jar + test + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + + copy-bdbstore-bin-resources + generate-resources + + copy-resources + + + ${qpid.home} + + + ${basedir}/.. + + bin/ + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + fix-bdb-script-permissions + package + + + + + + + + + + run + + + + + + diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java index ffa1d65ec2..0ec6a31253 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; +import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.qpid.server.configuration.ConfigurationEntryStore; @@ -41,7 +42,6 @@ import org.apache.qpid.server.logging.messages.BrokerMessages; 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.auth.TaskPrincipal; import javax.security.auth.Subject; @@ -52,6 +52,7 @@ public class Broker private volatile Thread _shutdownHookThread; private volatile IApplicationRegistry _applicationRegistry; private EventLogger _eventLogger; + private boolean _configuringOwnLogging = false; protected static class InitException extends RuntimeException { @@ -71,11 +72,20 @@ public class Broker } finally { - if (_applicationRegistry != null) + try { - _applicationRegistry.close(); + if (_applicationRegistry != null) + { + _applicationRegistry.close(); + } + } + finally + { + if (_configuringOwnLogging) + { + LogManager.shutdown(); + } } - } } @@ -166,6 +176,7 @@ public class Broker private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException { + _configuringOwnLogging = true; if (logConfigFile.exists() && logConfigFile.canRead()) { _eventLogger.message(BrokerMessages.LOG_CONFIG(logConfigFile.getAbsolutePath())); diff --git a/qpid/java/pom.xml b/qpid/java/pom.xml index 74042dd0bc..4dddd70851 100644 --- a/qpid/java/pom.xml +++ b/qpid/java/pom.xml @@ -54,7 +54,7 @@ Apache Qpid${line.separator}Copyright ${project.inceptionYear}-2014${line.separator}Apache Software Foundation${line.separator}This product includes software developed at Apache Software Foundation (http://www.apache.org/) - (java-mms.0-9|java-mms.0-9-1|java-mms.0-10|java-bdb.0-9|java-bdb.0-9-1|java-bdb.0-10|java-dby-mem.0-9|java-dby-mem.0-9-1|java-dby-mem.0-10) + (java-mms.0-9|java-mms.0-9-1|java-mms.0-10|java-bdb.0-9|java-bdb.0-9-1|java-bdb.0-10|java-dby.0-9|java-dby.0-9-1|java-dby.0-10|java-dby-mem.0-9|java-dby-mem.0-9-1|java-dby-mem.0-10) ${project.build.directory}/generated-licenses @@ -64,13 +64,22 @@ ${basedir} ${qpid.home}${file.separator}target${file.separator}qbtc-output ${project.build.directory}${file.separator}QPID_WORK + java-mms.0-10 - JavaTransientExcludes Java010Excludes + java + internal + Exception + BRK-1004 + ${qpid.home}${file.separator}bin${file.separator}qpid-server -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE + ${qpid.home}${file.separator}bin${file.separator}qpid-server.bat -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE + Excludes JavaExcludes ${profile}.excludes ${profile.specific.excludes} + JavaTransientExcludes Java010Excludes v0_10 AMQP_1_0 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests.xml false org.apache.qpid.server.store.MemoryMessageStore + true @@ -147,7 +156,11 @@ Qpid Test Profile Properties [profile] ${profile} - [profile.excludes] ${profile.excludes} + [profile.broker.language] ${profile.broker.language} + [profile.broker.type] ${profile.broker.type} + [profile.broker.command] ${profile.broker.command} + [profile.specific.excludes] ${profile.specific.excludes} + [profile.test.excludes] ${profile.test.excludes} [profile.broker.version] ${profile.broker.version} [profile.qpid.broker_default_amqp_protocol_excludes] ${profile.qpid.broker_default_amqp_protocol_excludes} [profile.broker.virtualhosts-config] ${profile.broker.virtualhosts-config} @@ -218,21 +231,22 @@ true 512M false - java - internal - Exception - BRK-1004 - ${qpid.home}${file.separator}bin${file.separator}qpid-server -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE - ${qpid.home}${file.separator}bin${file.separator}qpid-server.bat -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE - Excludes JavaExcludes ${profile}.excludes ${profile.excludes} + ${profile} + ${profile.broker.language} + ${profile.broker.type} + ${profile.broker.stopped} + ${profile.broker.ready} + ${profile.broker.command} + ${profile.broker.command.windows} + ${profile.test.excludes} ${profile.broker.version} ${profile.qpid.broker_default_amqp_protocol_excludes} ${profile.broker.virtualhosts-config} ${profile.broker.persistent} ${profile.messagestore.class.name} - ${profile} + ${profile.broker.clean.between.tests} ${qpid.home.qbtc.output} @@ -419,7 +433,7 @@ java-mms.0-10 - JavaTransientExcludes Java010Excludes + JavaTransientExcludes Java010Excludes v0_10 AMQP_1_0 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests.xml @@ -438,7 +452,7 @@ java-mms.0-9-1 - JavaTransientExcludes XAExcludes JavaPre010Excludes + JavaTransientExcludes XAExcludes JavaPre010Excludes v0_9_1 AMQP_1_0,AMQP_0_10 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests.xml @@ -457,7 +471,7 @@ java-mms.0-9 - JavaTransientExcludes XAExcludes JavaPre010Excludes + JavaTransientExcludes XAExcludes JavaPre010Excludes v0_9 AMQP_1_0,AMQP_0_10,AMQP_0_9_1 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests.xml @@ -476,7 +490,7 @@ java-bdb.0-10 - JavaPersistentExcludes Java010Excludes JavaBDBExcludes + JavaPersistentExcludes Java010Excludes JavaBDBExcludes v0_10 AMQP_1_0 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-bdb.xml @@ -495,7 +509,7 @@ java-bdb.0-9-1 - JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes + JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes v0_9_1 AMQP_1_0,AMQP_0_10 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-bdb.xml @@ -514,7 +528,7 @@ java-bdb.0-9 - JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes + JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes v0_9 AMQP_1_0,AMQP_0_10,AMQP_0_9_1 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-bdb.xml @@ -533,7 +547,7 @@ java-dby-mem.0-10 - JavaPersistentExcludes JavaDerbyExcludes Java010Excludes + JavaPersistentExcludes JavaDerbyExcludes Java010Excludes v0_10 AMQP_1_0 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby-mem.xml @@ -552,7 +566,7 @@ java-dby-mem.0-9-1 - JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes + JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes v0_9_1 AMQP_1_0,AMQP_0_10 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby-mem.xml @@ -571,7 +585,7 @@ java-dby-mem.0-9 - JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes + JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes v0_9 AMQP_1_0,AMQP_0_10,AMQP_0_9_1 ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby-mem.xml @@ -580,6 +594,63 @@ + + java-dby.0-10 + + + profile + java-dby.0-10 + + + + java-dby.0-10 + JavaPersistentExcludes JavaDerbyExcludes Java010Excludes + v0_10 + AMQP_1_0 + ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml + true + org.apache.qpid.server.store.derby.DerbyMessageStore + + + + + java-dby.0-9-1 + + + profile + java-dby.0-9-1 + + + + java-dby.0-9-1 + JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes + v0_9_1 + AMQP_1_0,AMQP_0_10 + ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml + true + org.apache.qpid.server.store.derby.DerbyMessageStore + + + + + java-dby.0-9 + + + profile + java-dby.0-9 + + + + java-dby.0-9 + JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes + v0_9 + AMQP_1_0,AMQP_0_10,AMQP_0_9_1 + ${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml + true + org.apache.qpid.server.store.derby.DerbyMessageStore + + + diff --git a/qpid/java/qpid-perftests-systests/pom.xml b/qpid/java/qpid-perftests-systests/pom.xml index 6856fd9d17..4557e903ff 100644 --- a/qpid/java/qpid-perftests-systests/pom.xml +++ b/qpid/java/qpid-perftests-systests/pom.xml @@ -86,6 +86,20 @@ runtime + + org.apache.qpid + qpid-bdbstore + ${project.version} + test + true + + + + com.sleepycat + je + test + true + -- cgit v1.2.1 From f96b8856ba95a9d6d29981b8f41fa9319d9a6813 Mon Sep 17 00:00:00 2001 From: Alex Rudyy Date: Fri, 21 Mar 2014 12:46:13 +0000 Subject: QPID-5048: new maven enforcer plugin rule for checking license files Patch supplied by Andrew MacBean git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579936 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml | 93 +++++++++++++ .../rule/RequireFileContentsAreEquivalent.java | 104 ++++++++++++++ .../rule/TestRequireFileContentsAreEquivalent.java | 149 +++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml create mode 100644 qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java create mode 100644 qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java (limited to 'qpid/java') diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml b/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml new file mode 100644 index 0000000000..47a393b888 --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml @@ -0,0 +1,93 @@ + + + + 4.0.0 + + org.apache.qpid.enforcer + qpid-enforcer-plugin-rules + 1.0-SNAPSHOT + Qpid Maven Enforcer Plugin Rules + Custom maven enforcer plugin rules for the Qpid maven build. + + + 1.3.1 + 2.2.1 + 1.5.5 + 4.11 + + + + + org.apache.maven.enforcer + enforcer-rules + ${api.version} + + + org.apache.commons + commons-io + 1.3.2 + + + org.apache.maven.enforcer + enforcer-api + ${api.version} + + + org.apache.maven + maven-project + ${maven.version} + + + org.apache.maven + maven-core + ${maven.version} + + + org.apache.maven + maven-artifact + ${maven.version} + + + org.apache.maven + maven-plugin-api + ${maven.version} + + + org.codehaus.plexus + plexus-container-default + ${plexus.container.version} + + + junit + junit + ${junit.version} + test + + + org.apache.maven.enforcer + enforcer-rules + ${api.version} + test-jar + test + + + + + + + diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java b/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java new file mode 100644 index 0000000000..11186a5c7f --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.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.maven.enforcer.rule; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.enforcer.rule.api.EnforcerRule; +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; +import org.apache.maven.plugins.enforcer.AbstractStandardEnforcerRule; + +public class RequireFileContentsAreEquivalent extends AbstractStandardEnforcerRule +{ + + final static String WHITESPACE_REGEX = "\\s+"; + final static String EMPTY_STRING = ""; + + File[] files; + + @Override + public void execute(final EnforcerRuleHelper enforcerRuleHelper) throws EnforcerRuleException + { + if (files.length < 2) + { + throw new EnforcerRuleException("The file list must contain at least two files for comparison."); + } + + boolean success = true; + boolean firstTime = true; + String referenceContent = null; + + for (final File file : files) + { + try + { + final String fileContent = FileUtils.readFileToString(file); + if (firstTime) + { + referenceContent = fileContent; + firstTime = false; + } + else if (referenceContent != null && fileContent != null) + { + final String strippedReferenceContent = referenceContent.replaceAll(WHITESPACE_REGEX, EMPTY_STRING); + final String strippedFileContent = fileContent.replaceAll(WHITESPACE_REGEX, EMPTY_STRING); + if (!strippedReferenceContent.equalsIgnoreCase(strippedFileContent)) + { + success = false; + break; + } + } + else + { + throw new EnforcerRuleException("Unable to read file contents"); + } + } + catch (final IOException ioe) + { + throw new EnforcerRuleException("Cannot process file : " + file.getName(), ioe); + } + } + + if (!success) + { + throw new EnforcerRuleException("Files specified are not equal in content"); + } + } + + @Override + public String getCacheId() + { + return Integer.toString(Arrays.hashCode(files)); + } + + @Override + public boolean isCacheable() + { + return true; + } + + @Override + public boolean isResultValid(EnforcerRule arg0) + { + return true; + } + +} diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java b/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java new file mode 100644 index 0000000000..7a5d214972 --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java @@ -0,0 +1,149 @@ +/** + 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.maven.enforcer.rule; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.plugins.enforcer.EnforcerTestUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class TestRequireFileContentsAreEquivalent +{ + final RequireFileContentsAreEquivalent rule = new RequireFileContentsAreEquivalent(); + + final static String TEST_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + final static String TEST_TEXT_WHITESPACE = "Lorem ipsum dolor sit amet,\n consectetur adipiscing \t elit. "; + final static String ALTERNATE_TEST_TEXT_WHITESPACE = " Lorem \t ip sum dolor \n sit amet,\n consectetur adipiscing \t elit."; + final static String DIFFERENT_TEST_TEXT = "Donec velit felis, semper dapibus mattis vitae"; + + @Test + public void testDifferentContentFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, DIFFERENT_TEST_TEXT); + + rule.files = new File[] + { f1, f2 }; + + try + { + rule.execute(EnforcerTestUtils.getHelper()); + Assert.fail("Files with different content should have failed enforcer rule"); + } + catch (final EnforcerRuleException ere) + { + // do nothing + } + } + + @Test + public void testIdenticalContentFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT); + + rule.files = new File[] + { f1, f2 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testUsingOneFileTwice() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + + rule.files = new File[] + { f1, f1 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testSimilarFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT_WHITESPACE); + + rule.files = new File[] + { f1, f2 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testMultipleFilesOneDifferent() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT_WHITESPACE); + final File f3 = createTestFile(3, ALTERNATE_TEST_TEXT_WHITESPACE); + final File f4 = createTestFile(4, DIFFERENT_TEST_TEXT); + + rule.files = new File[] + { f1, f2, f3, f4 }; + + try + { + rule.execute(EnforcerTestUtils.getHelper()); + Assert.fail("Files with different content should have failed enforcer rule"); + } + catch (final EnforcerRuleException ere) + { + // do nothing + } + } + + @Test + public void testMultipleFilesAllSimilar() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT); + final File f3 = createTestFile(3, TEST_TEXT_WHITESPACE); + final File f4 = createTestFile(4, ALTERNATE_TEST_TEXT_WHITESPACE); + + rule.files = new File[] + { f1, f2, f3, f4 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @After + public void deleteTestFiles() throws Exception + { + for (File file : rule.files) + { + if (file.exists()) + { + file.delete(); + } + } + } + + private File createTestFile(final int id, final String content) throws IOException + { + final File file = File.createTempFile(TestRequireFileContentsAreEquivalent.class.getName() + + "-testfile" + id, "tmp"); + file.deleteOnExit(); + FileUtils.writeStringToFile(file, content); + return file; + } +} -- cgit v1.2.1 From d77447d7230dd29d7dc9ee0575caf1997ec3a7a6 Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Fri, 21 Mar 2014 17:16:34 +0000 Subject: QPID-5634: [Java Broker] Remove support for AccessPlugins at the level of the virtualhost. Introduce supports for ACLs rules that include virtualhost predicate. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1579986 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/exchange/AbstractExchange.java | 2 +- .../org/apache/qpid/server/model/VirtualHost.java | 7 - .../server/model/adapter/VirtualHostAdapter.java | 7 - .../qpid/server/security/SecurityManager.java | 366 ++++++------- .../server/security/access/ObjectProperties.java | 64 +-- .../security/access/OperationLoggingDetails.java | 36 ++ .../server/virtualhost/AbstractVirtualHost.java | 2 +- .../qpid/server/security/SecurityManagerTest.java | 571 +++++++++++++++++++++ .../AbstractDurableConfigurationStoreTestCase.java | 1 - .../access/plugins/DefaultAccessControlTest.java | 16 +- .../security/access/plugins/RuleSetTest.java | 105 +++- .../protocol/v0_10/ServerSessionDelegate.java | 2 +- .../qpid/server/protocol/v0_8/AMQChannel.java | 5 +- .../v0_8/handler/ConnectionOpenMethodHandler.java | 3 +- .../plugin/servlet/rest/MessageServlet.java | 6 +- .../server/jmx/MBeanInvocationHandlerImpl.java | 25 +- .../server/logging/AccessControlLoggingTest.java | 3 +- .../server/security/acl/AbstractACLTestCase.java | 36 +- .../server/security/acl/ExhaustiveACLTest.java | 53 +- .../server/security/acl/ExternalACLJMXTest.java | 78 ++- .../qpid/server/security/acl/ExternalACLTest.java | 214 +++++--- .../qpid/systest/rest/acl/BrokerACLTest.java | 3 +- .../qpid/systest/rest/acl/ExchangeRestACLTest.java | 3 +- .../qpid/systest/rest/acl/GroupRestACLTest.java | 12 +- .../qpid/systest/rest/acl/LogViewerACLTest.java | 3 +- .../qpid/systest/rest/acl/QueueRestACLTest.java | 3 +- .../rest/acl/UserPreferencesRestACLTest.java | 2 +- .../qpid/systest/rest/acl/UserRestACLTest.java | 9 +- 28 files changed, 1122 insertions(+), 515 deletions(-) create mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index a30806d810..25f20ba1ee 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -612,7 +612,7 @@ public abstract class AbstractExchange> } // Check access - _virtualHost.getSecurityManager().authoriseUnbind(this, bindingKey, queue); + _virtualHost.getSecurityManager().authoriseUnbind(binding); BindingImpl b = _bindingsMap.remove(new BindingIdentifier(bindingKey,queue)); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java index 4aec9b38a0..ee10ecfc8b 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java @@ -22,7 +22,6 @@ package org.apache.qpid.server.model; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.message.MessageInstance; -import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.MessageStore; import java.security.AccessControlException; @@ -179,12 +178,6 @@ public interface VirtualHost> extends ConfiguredObject< void executeTransaction(TransactionalOperation op); - /** - * A temporary hack to expose host security manager. - * TODO We need to add and implement an authorization provider configured object instead - */ - SecurityManager getSecurityManager(); - // TODO - remove this TaskExecutor getTaskExecutor(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java index 72b316c784..6dbc2eea85 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java @@ -49,7 +49,6 @@ import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.ConflationQueue; -import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.MessageStore; @@ -1125,12 +1124,6 @@ public final class VirtualHostAdapter extends AbstractConfiguredObject _plugins = new ConcurrentHashMap(); + private final boolean _managementMode; + private final Broker _broker; - private ConcurrentHashMap _globalPlugins = new ConcurrentHashMap(); - private ConcurrentHashMap _hostPlugins = new ConcurrentHashMap(); - - private boolean _managementMode; - - private Broker _broker; + private final ConcurrentHashMap _publishAccessCheckCache = new ConcurrentHashMap(); - /* - * Used by the Broker. - */ - public SecurityManager(Broker broker, boolean managementMode) + public SecurityManager(Broker broker, boolean managementMode) { _managementMode = managementMode; _broker = broker; } - /* - * Used by the VirtualHost to allow deferring to the broker level security plugins if required. - */ - public SecurityManager(SecurityManager parent, String aclFile, String vhostName) - { - _managementMode = parent._managementMode; - _broker = parent._broker; - if(!_managementMode) - { - configureVirtualHostAclPlugin(aclFile, vhostName); - - // our global plugins are the parent's host plugins - _globalPlugins = parent._hostPlugins; - } - } - public static Subject getSubjectWithAddedSystemRights() { Subject subject = Subject.getSubject(AccessController.getContext()); @@ -135,50 +108,11 @@ public class SecurityManager implements ConfigurationChangeListener return subject; } - private void configureVirtualHostAclPlugin(String aclFile, String vhostName) - { - if(aclFile != null) - { - Map attributes = new HashMap(); - - attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); - attributes.put(FileAccessControlProviderConstants.PATH, aclFile); - - for (AccessControlFactory provider : (new QpidServiceLoader()).instancesOf(AccessControlFactory.class)) - { - AccessControl accessControl = provider.createInstance(attributes, _broker); - accessControl.open(); - if(accessControl != null) - { - String pluginTypeName = getPluginTypeName(accessControl); - _hostPlugins.put(pluginTypeName, accessControl); - - if(_logger.isDebugEnabled()) - { - _logger.debug("Added access control to host plugins with name: " + vhostName); - } - - break; - } - } - } - - if(_logger.isDebugEnabled()) - { - _logger.debug("Configured " + _hostPlugins.size() + " access control plugins"); - } - } - private String getPluginTypeName(AccessControl accessControl) { return accessControl.getClass().getName(); } - public static Logger getLogger() - { - return _logger; - } - public static boolean isSystemProcess() { Subject subject = Subject.getSubject(AccessController.getContext()); @@ -234,71 +168,14 @@ public class SecurityManager implements ConfigurationChangeListener return true; } - Map remainingPlugins = _globalPlugins.isEmpty() - ? Collections.emptyMap() - : _hostPlugins.isEmpty() ? _globalPlugins : new HashMap(_globalPlugins); - - if(!_hostPlugins.isEmpty()) - { - for (Entry hostEntry : _hostPlugins.entrySet()) - { - // Create set of global only plugins - AccessControl globalPlugin = remainingPlugins.get(hostEntry.getKey()); - if (globalPlugin != null) - { - remainingPlugins.remove(hostEntry.getKey()); - } - - Result host = checker.allowed(hostEntry.getValue()); - - if (host == Result.DENIED) - { - // Something vetoed the access, we're done - return false; - } - - // host allow overrides global allow, so only check global on abstain or defer - if (host != Result.ALLOWED) - { - if (globalPlugin == null) - { - if (host == Result.DEFER) - { - host = hostEntry.getValue().getDefault(); - } - if (host == Result.DENIED) - { - return false; - } - } - else - { - Result global = checker.allowed(globalPlugin); - if (global == Result.DEFER) - { - global = globalPlugin.getDefault(); - } - if (global == Result.ABSTAIN && host == Result.DEFER) - { - global = hostEntry.getValue().getDefault(); - } - if (global == Result.DENIED) - { - return false; - } - } - } - } - } - - for (AccessControl plugin : remainingPlugins.values()) + for (AccessControl plugin : _plugins.values()) { Result remaining = checker.allowed(plugin); - if (remaining == Result.DEFER) + if (remaining == Result.DEFER) { remaining = plugin.getDefault(); } - if (remaining == Result.DENIED) + if (remaining == Result.DENIED) { return false; } @@ -308,28 +185,23 @@ public class SecurityManager implements ConfigurationChangeListener return true; } - public void authoriseCreateBinding(BindingImpl binding) + public void authoriseCreateBinding(final BindingImpl binding) { - final ExchangeImpl exch = binding.getExchange(); - final AMQQueue queue = binding.getAMQQueue(); - final String bindingKey = binding.getBindingKey(); - - boolean allowed = - checkAllPlugins(new AccessCheck() + boolean allowed = checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(BIND, EXCHANGE, new ObjectProperties(exch, queue, bindingKey)); + return plugin.authorise(BIND, EXCHANGE, new ObjectProperties(binding)); } }); if(!allowed) { - throw new AccessControlException("Permission denied: binding " + bindingKey); + throw new AccessControlException("Permission denied: binding " + binding.getBindingKey()); } } - public void authoriseMethod(final Operation operation, final String componentName, final String methodName) + public void authoriseMethod(final Operation operation, final String componentName, final String methodName, final String virtualHostName) { boolean allowed = checkAllPlugins(new AccessCheck() { @@ -339,8 +211,11 @@ public class SecurityManager implements ConfigurationChangeListener properties.setName(methodName); if (componentName != null) { - // Only set the property if there is a component name - properties.put(ObjectProperties.Property.COMPONENT, componentName); + properties.put(ObjectProperties.Property.COMPONENT, componentName); + } + if (virtualHostName != null) + { + properties.put(ObjectProperties.Property.VIRTUALHOST_NAME, virtualHostName); } return plugin.authorise(operation, METHOD, properties); } @@ -367,15 +242,19 @@ public class SecurityManager implements ConfigurationChangeListener public void authoriseCreateConnection(final AMQConnectionModel connection) { + final String virtualHostName = connection.getVirtualHostName(); if(!checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(Operation.ACCESS, VIRTUALHOST, ObjectProperties.EMPTY); + // We put the name into the properties under both name and virtualhost_name so the user may express predicates using either. + ObjectProperties properties = new ObjectProperties(virtualHostName); + properties.put(Property.VIRTUALHOST_NAME, virtualHostName); + return plugin.authorise(Operation.ACCESS, VIRTUALHOST, properties); } })) { - throw new AccessControlException("Permission denied: " + connection.getVirtualHostName()); + throw new AccessControlException("Permission denied: " + virtualHostName); } } @@ -403,10 +282,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(CREATE, EXCHANGE, new ObjectProperties(exchange.isAutoDelete(), - exchange.isDurable(), - exchangeName, - exchange.getTypeName())); + return plugin.authorise(CREATE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -421,11 +297,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(CREATE, QUEUE, new ObjectProperties(queue.getAttribute(Queue.LIFETIME_POLICY) != LifetimePolicy.PERMANENT, - Boolean.TRUE.equals(queue.getAttribute(Queue.DURABLE)), - queue.getAttribute(Queue.EXCLUSIVE) != ExclusivityPolicy.NONE, - queueName, - queue.getOwner())); + return plugin.authorise(CREATE, QUEUE, new ObjectProperties(queue)); } })) { @@ -470,7 +342,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(UPDATE, EXCHANGE, new ObjectProperties(exchange.getName())); + return plugin.authorise(UPDATE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -484,7 +356,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(DELETE, EXCHANGE, new ObjectProperties(exchange.getName())); + return plugin.authorise(DELETE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -522,39 +394,15 @@ public class SecurityManager implements ConfigurationChangeListener } } - private ConcurrentHashMap> _immediatePublishPropsCache - = new ConcurrentHashMap>(); - private ConcurrentHashMap> _publishPropsCache - = new ConcurrentHashMap>(); - - public void authorisePublish(final boolean immediate, String routingKey, String exchangeName) + public void authorisePublish(final boolean immediate, String routingKey, String exchangeName, String virtualHostName) { - if(routingKey == null) + PublishAccessCheckCacheEntry key = new PublishAccessCheckCacheEntry(immediate, routingKey, exchangeName, virtualHostName); + PublishAccessCheck check = _publishAccessCheckCache.get(key); + if (check == null) { - routingKey = ""; + check = new PublishAccessCheck(new ObjectProperties(virtualHostName, exchangeName, routingKey, immediate)); + _publishAccessCheckCache.putIfAbsent(key, check); } - if(exchangeName == null) - { - exchangeName = ""; - } - PublishAccessCheck check; - ConcurrentHashMap> cache = - immediate ? _immediatePublishPropsCache : _publishPropsCache; - - ConcurrentHashMap exchangeMap = cache.get(exchangeName); - if(exchangeMap == null) - { - cache.putIfAbsent(exchangeName, new ConcurrentHashMap()); - exchangeMap = cache.get(exchangeName); - } - - check = exchangeMap.get(routingKey); - if(check == null) - { - check = new PublishAccessCheck(new ObjectProperties(exchangeName, routingKey, immediate)); - exchangeMap.put(routingKey, check); - } - if(!checkAllPlugins(check)) { throw new AccessControlException("Permission denied, publish to: exchange-name '" + exchangeName + "'"); @@ -575,17 +423,17 @@ public class SecurityManager implements ConfigurationChangeListener } } - public void authoriseUnbind(final ExchangeImpl exch, final String routingKey, final AMQQueue queue) + public void authoriseUnbind(final BindingImpl binding) { if(! checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(UNBIND, EXCHANGE, new ObjectProperties(exch, queue, routingKey)); + return plugin.authorise(UNBIND, EXCHANGE, new ObjectProperties(binding)); } })) { - throw new AccessControlException("Permission denied: unbinding " + routingKey); + throw new AccessControlException("Permission denied: unbinding " + binding.getBindingKey()); } } @@ -618,29 +466,29 @@ public class SecurityManager implements ConfigurationChangeListener { if(newState == State.ACTIVE) { - synchronized (_hostPlugins) + synchronized (_plugins) { AccessControl accessControl = ((AccessControlProvider)object).getAccessControl(); String pluginTypeName = getPluginTypeName(accessControl); - _hostPlugins.put(pluginTypeName, accessControl); + _plugins.put(pluginTypeName, accessControl); } } else if(newState == State.DELETED) { - synchronized (_hostPlugins) + synchronized (_plugins) { AccessControl control = ((AccessControlProvider)object).getAccessControl(); String pluginTypeName = getPluginTypeName(control); // Remove the type->control mapping for this type key only if the // given control is actually referred to. - if(_hostPlugins.containsValue(control)) + if(_plugins.containsValue(control)) { // If we are removing this control, check if another of the same // type already exists on the broker and use it in instead. AccessControl other = null; - Collection providers = _broker.getAccessControlProviders(); + Collection> providers = _broker.getAccessControlProviders(); for(AccessControlProvider p : providers) { if(p == object || p.getState() != State.ACTIVE) @@ -660,12 +508,12 @@ public class SecurityManager implements ConfigurationChangeListener if(other != null) { //Another control of this type was found, use it instead - _hostPlugins.replace(pluginTypeName, control, other); + _plugins.replace(pluginTypeName, control, other); } else { //No other was found, remove the type entirely - _hostPlugins.remove(pluginTypeName); + _plugins.remove(pluginTypeName); } } } @@ -718,4 +566,90 @@ public class SecurityManager implements ConfigurationChangeListener }); } + public static class PublishAccessCheckCacheEntry + { + private final boolean _immediate; + private final String _routingKey; + private final String _exchangeName; + private final String _virtualHostName; + + public PublishAccessCheckCacheEntry(boolean immediate, String routingKey, String exchangeName, String virtualHostName) + { + super(); + _immediate = immediate; + _routingKey = routingKey; + _exchangeName = exchangeName; + _virtualHostName = virtualHostName; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((_exchangeName == null) ? 0 : _exchangeName.hashCode()); + result = prime * result + (_immediate ? 1231 : 1237); + result = prime * result + ((_routingKey == null) ? 0 : _routingKey.hashCode()); + result = prime * result + ((_virtualHostName == null) ? 0 : _virtualHostName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + PublishAccessCheckCacheEntry other = (PublishAccessCheckCacheEntry) obj; + if (_exchangeName == null) + { + if (other._exchangeName != null) + { + return false; + } + } + else if (!_exchangeName.equals(other._exchangeName)) + { + return false; + } + if (_immediate != other._immediate) + { + return false; + } + if (_routingKey == null) + { + if (other._routingKey != null) + { + return false; + } + } + else if (!_routingKey.equals(other._routingKey)) + { + return false; + } + if (_virtualHostName == null) + { + if (other._virtualHostName != null) + { + return false; + } + } + else if (!_virtualHostName.equals(other._virtualHostName)) + { + return false; + } + return true; + } + + + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java index ae0241314f..6122cef5c1 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java @@ -26,8 +26,10 @@ import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.queue.AMQQueue; /** @@ -39,7 +41,7 @@ import org.apache.qpid.server.queue.AMQQueue; */ public class ObjectProperties { - public static final String STAR= "*"; + public static final String WILD_CARD = "*"; public static final ObjectProperties EMPTY = new ObjectProperties(); @@ -65,7 +67,8 @@ public class ObjectProperties PACKAGE, CLASS, FROM_NETWORK, - FROM_HOSTNAME; + FROM_HOSTNAME, + VIRTUALHOST_NAME; private static final Map _canonicalNameToPropertyMap = new HashMap(); @@ -152,60 +155,49 @@ public class ObjectProperties { put(Property.OWNER, queue.getOwner()); } - + put(Property.VIRTUALHOST_NAME, queue.getParent(VirtualHost.class).getName()); } - public ObjectProperties(ExchangeImpl exch, AMQQueue queue, String routingKey) + public ObjectProperties(BindingImpl binding) { - this(queue); + ExchangeImpl exch = binding.getExchange(); + AMQQueue queue = binding.getAMQQueue(); + String routingKey = binding.getBindingKey(); setName(exch.getName()); - put(Property.QUEUE_NAME, queue.getName()); + put(Property.QUEUE_NAME, queue.getName()); put(Property.ROUTING_KEY, routingKey); - } - - public ObjectProperties(String exchangeName, String routingKey, Boolean immediate) - { - this(exchangeName, routingKey); + put(Property.VIRTUALHOST_NAME, queue.getParent(VirtualHost.class).getName()); - put(Property.IMMEDIATE, immediate); + // The temporary attribute (inherited from the binding's queue) seems to exist to allow the user to + // express rules about the binding of temporary queues (whose names cannot be predicted). + put(Property.TEMPORARY, queue.getLifetimePolicy() != LifetimePolicy.PERMANENT); + put(Property.DURABLE, queue.isDurable()); } - public ObjectProperties(String exchangeName, String routingKey) + public ObjectProperties(String virtualHostName, String exchangeName, String routingKey, Boolean immediate) { super(); setName(exchangeName); put(Property.ROUTING_KEY, routingKey); + put(Property.IMMEDIATE, immediate); + put(Property.VIRTUALHOST_NAME, virtualHostName); } - public ObjectProperties(Boolean autoDelete, Boolean durable, String exchangeName, - String exchangeType) - { - super(); - - setName(exchangeName); - - put(Property.AUTO_DELETE, autoDelete); - put(Property.TEMPORARY, autoDelete); - put(Property.DURABLE, durable); - put(Property.TYPE, exchangeType); - } - - public ObjectProperties(Boolean autoDelete, Boolean durable, Boolean exclusive, - String queueName, String owner) + public ObjectProperties(ExchangeImpl exchange) { super(); - setName(queueName); + setName(exchange.getName()); - put(Property.AUTO_DELETE, autoDelete); - put(Property.TEMPORARY, autoDelete); - put(Property.DURABLE, durable); - put(Property.EXCLUSIVE, exclusive); - put(Property.OWNER, owner); + put(Property.AUTO_DELETE, exchange.isAutoDelete()); + put(Property.TEMPORARY, exchange.getLifetimePolicy() != LifetimePolicy.PERMANENT); + put(Property.DURABLE, exchange.isDurable()); + put(Property.TYPE, exchange.getTypeName()); + put(Property.VIRTUALHOST_NAME, exchange.getParent(VirtualHost.class).getName()); } public ObjectProperties(Boolean exclusive, Boolean noAck, Boolean noLocal, Boolean nowait, AMQQueue queue) @@ -283,8 +275,8 @@ public class ObjectProperties { return (StringUtils.isEmpty(ruleValue) || StringUtils.equals(thisValue, ruleValue)) - || ruleValue.equals(STAR) - || (ruleValue.endsWith(STAR) + || ruleValue.equals(WILD_CARD) + || (ruleValue.endsWith(WILD_CARD) && thisValue != null && thisValue.length() >= ruleValue.length() - 1 && thisValue.startsWith(ruleValue.substring(0, ruleValue.length() - 1))); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java index a683199abc..f36695cb86 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java @@ -31,6 +31,42 @@ public class OperationLoggingDetails extends ObjectProperties _description = description; } + @Override + public int hashCode() + { + return super.hashCode() + ((_description == null) ? 0 : _description.hashCode()); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!super.equals(obj)) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + OperationLoggingDetails other = (OperationLoggingDetails) obj; + if (_description == null) + { + if (other._description != null) + { + return false; + } + } + else if (!_description.equals(other._description)) + { + return false; + } + return true; + } + @Override public String toString() { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java index a4719f6058..2f1939be39 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java @@ -156,7 +156,7 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg _eventLogger.message(VirtualHostMessages.CREATED(_name)); - _securityManager = new SecurityManager(parentSecurityManager, _vhostConfig.getConfig().getString("security.acl"), _name); + _securityManager = parentSecurityManager; _connectionRegistry = new ConnectionRegistry(); _connectionRegistry.addRegistryChangeListener(this); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java new file mode 100644 index 0000000000..e27981d22c --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java @@ -0,0 +1,571 @@ +/* + * + * 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; + +import static org.apache.qpid.server.security.access.ObjectType.BROKER; +import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; +import static org.apache.qpid.server.security.access.Operation.ACCESS_LOGS; +import static org.apache.qpid.server.security.access.Operation.PUBLISH; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.security.AccessControlException; + +import org.apache.qpid.server.binding.BindingImpl; +import org.apache.qpid.server.consumer.ConsumerImpl; +import org.apache.qpid.server.exchange.ExchangeImpl; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.queue.AMQQueue; +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.OperationLoggingDetails; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SecurityManagerTest extends QpidTestCase +{ + private static final String TEST_EXCHANGE_TYPE = "testExchangeType"; + private static final String TEST_VIRTUAL_HOST = "testVirtualHost"; + private static final String TEST_EXCHANGE = "testExchange"; + private static final String TEST_QUEUE = "testQueue"; + + private AccessControl _accessControl; + private SecurityManager _securityManager; + private VirtualHost _virtualHost; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _accessControl = mock(AccessControl.class); + _virtualHost = mock(VirtualHost.class); + + AccessControlProvider aclProvider = mock(AccessControlProvider.class); + when(aclProvider.getAccessControl()).thenReturn(_accessControl); + + when(_virtualHost.getName()).thenReturn(TEST_VIRTUAL_HOST); + + _securityManager = new SecurityManager(mock(Broker.class), false); + _securityManager.stateChanged(aclProvider, State.INITIALISING, State.ACTIVE); + } + + public void testAuthoriseCreateBinding() + { + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + BindingImpl binding = mock(BindingImpl.class); + when(binding.getExchange()).thenReturn(exchange); + when(binding.getAMQQueue()).thenReturn(queue); + when(binding.getBindingKey()).thenReturn("bindingKey"); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.QUEUE_NAME, TEST_QUEUE); + properties.put(Property.ROUTING_KEY, "bindingKey"); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateBinding(binding); + verify(_accessControl).authorise(eq(Operation.BIND), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateBinding(binding); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.BIND), eq(ObjectType.EXCHANGE), eq(properties)); + } + + + public void testAuthoriseMethod() + { + ObjectProperties properties = new ObjectProperties("testMethod"); + properties.put(ObjectProperties.Property.COMPONENT, "testComponent"); + properties.put(ObjectProperties.Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseMethod(Operation.UPDATE, "testComponent", "testMethod", TEST_VIRTUAL_HOST); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.METHOD), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseMethod(Operation.UPDATE, "testComponent", "testMethod", TEST_VIRTUAL_HOST); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.METHOD), eq(properties)); + } + + public void testAccessManagement() + { + configureAccessPlugin(Result.ALLOWED); + _securityManager.accessManagement(); + verify(_accessControl).authorise(Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.accessManagement(); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + } + + public void testAuthoriseCreateConnection() + { + AMQConnectionModel connection = mock(AMQConnectionModel.class); + when(connection.getVirtualHostName()).thenReturn(TEST_VIRTUAL_HOST); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_VIRTUAL_HOST); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateConnection(connection); + verify(_accessControl).authorise(eq(Operation.ACCESS), eq(ObjectType.VIRTUALHOST), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateConnection(connection); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.ACCESS), eq(ObjectType.VIRTUALHOST), eq(properties)); + } + + public void testAuthoriseCreateConsumer() + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + ConsumerImpl consumer = mock(ConsumerImpl.class); + when(consumer.getMessageSource()).thenReturn(queue); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_QUEUE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, false); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + properties.put(Property.EXCLUSIVE, false); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateConsumer(consumer); + verify(_accessControl).authorise(eq(Operation.CONSUME), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateConsumer(consumer); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CONSUME), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseCreateExchange() + { + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateExchange(exchange); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateExchange(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseCreateQueue() + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateQueue(queue); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateQueue(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseDeleteQueue() + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseDelete(queue); + verify(_accessControl).authorise(eq(Operation.DELETE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseDelete(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.DELETE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseUpdateQueue() + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUpdate(queue); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUpdate(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseUpdateExchange() + { + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUpdate(exchange); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUpdate(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseDeleteExchange() + { + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseDelete(exchange); + verify(_accessControl).authorise(eq(Operation.DELETE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseDelete(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.DELETE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseGroupOperation() + { + ObjectProperties properties = new ObjectProperties("testGroup"); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseGroupOperation(Operation.CREATE, "testGroup"); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.GROUP), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseGroupOperation(Operation.CREATE, "testGroup"); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.GROUP), eq(properties)); + } + + public void testAuthoriseUserOperation() + { + ObjectProperties properties = new ObjectProperties("testUser"); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUserOperation(Operation.CREATE, "testUser"); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.USER), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUserOperation(Operation.CREATE, "testUser"); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.USER), eq(properties)); + } + + public void testAuthorisePublish() + { + String routingKey = "routingKey"; + String exchangeName = "exchangeName"; + boolean immediate = true; + ObjectProperties properties = new ObjectProperties(TEST_VIRTUAL_HOST, exchangeName, routingKey, immediate); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authorisePublish(immediate, routingKey, exchangeName, TEST_VIRTUAL_HOST); + verify(_accessControl).authorise(eq(Operation.PUBLISH), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authorisePublish(immediate, routingKey, exchangeName, TEST_VIRTUAL_HOST); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.PUBLISH), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthorisePurge() + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authorisePurge(queue); + verify(_accessControl).authorise(eq(Operation.PURGE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authorisePurge(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.PURGE), eq(ObjectType.QUEUE), eq(properties)); + } + + + public void testAuthoriseUnbind() + { + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + + AMQQueue queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + BindingImpl binding = mock(BindingImpl.class); + when(binding.getExchange()).thenReturn(exchange); + when(binding.getAMQQueue()).thenReturn(queue); + when(binding.getBindingKey()).thenReturn("bindingKey"); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.QUEUE_NAME, TEST_QUEUE); + properties.put(Property.ROUTING_KEY, "bindingKey"); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUnbind(binding); + verify(_accessControl).authorise(eq(Operation.UNBIND), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUnbind(binding); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UNBIND), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseConfiguringBroker() + { + OperationLoggingDetails properties = new OperationLoggingDetails("create virtualhost 'test'"); + + configureAccessPlugin(Result.ALLOWED); + assertTrue(_securityManager.authoriseConfiguringBroker("test", VirtualHost.class, Operation.CREATE)); + verify(_accessControl).authorise(eq(Operation.CONFIGURE), eq(ObjectType.BROKER), eq(properties)); + + configureAccessPlugin(Result.DENIED); + assertFalse(_securityManager.authoriseConfiguringBroker("test", VirtualHost.class, Operation.CREATE)); + verify(_accessControl, times(2)).authorise(eq(Operation.CONFIGURE), eq(ObjectType.BROKER), eq(properties)); + } + + public void testAuthoriseLogsAccess() + { + configureAccessPlugin(Result.ALLOWED); + assertTrue(_securityManager.authoriseLogsAccess()); + verify(_accessControl).authorise(ACCESS_LOGS, BROKER, ObjectProperties.EMPTY); + + configureAccessPlugin(Result.DENIED); + assertFalse(_securityManager.authoriseLogsAccess()); + verify(_accessControl, times(2)).authorise(ACCESS_LOGS, BROKER, ObjectProperties.EMPTY); + } + + private void configureAccessPlugin(Result result) + { + when(_accessControl.authorise(any(Operation.class), any(ObjectType.class), any(ObjectProperties.class))).thenReturn(result); + } + + private ObjectProperties createExpectedExchangeObjectProperties() + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, false); + properties.put(Property.TEMPORARY, true); + properties.put(Property.DURABLE, false); + properties.put(Property.TYPE, TEST_EXCHANGE_TYPE); + return properties; + } + + private ObjectProperties createExpectedQueueObjectProperties() + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_QUEUE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, true); + properties.put(Property.TEMPORARY, true); + properties.put(Property.DURABLE, false); + properties.put(Property.EXCLUSIVE, false); + return properties; + } + + + +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java index 650b22ff51..04c2f974c9 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java @@ -108,7 +108,6 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest _queueEntryRecoveryHandler = mock(TransactionLogRecoveryHandler.QueueEntryRecoveryHandler.class); _dtxRecordRecoveryHandler = mock(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler.class); _virtualHost = mock(VirtualHost.class); - when(_virtualHost.getSecurityManager()).thenReturn(mock(org.apache.qpid.server.security.SecurityManager.class)); when(_messageStoreRecoveryHandler.begin()).thenReturn(_storedMessageRecoveryHandler); when(_logRecoveryHandler.begin(any(MessageStore.class))).thenReturn(_queueEntryRecoveryHandler); diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java index 74ea7639ff..72dadb736f 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java @@ -20,7 +20,9 @@ */ package org.apache.qpid.server.security.access.plugins; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -182,7 +184,7 @@ public class DefaultAccessControlTest extends TestCase final RuleSet rs = new RuleSet(mock(EventLoggerProvider.class)); // grant user4 access right on any method in any component - rs.grant(1, "user4", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, new ObjectProperties(ObjectProperties.STAR)); + rs.grant(1, "user4", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, new ObjectProperties(ObjectProperties.WILD_CARD)); configureAccessControl(rs); Subject.doAs(TestPrincipalUtils.createTestSubject("user4"), new PrivilegedAction() { @@ -208,7 +210,7 @@ public class DefaultAccessControlTest extends TestCase final RuleSet rs = new RuleSet(mock(EventLoggerProvider.class)); // grant user5 access right on any methods in "Test" component - ObjectProperties ruleProperties = new ObjectProperties(ObjectProperties.STAR); + ObjectProperties ruleProperties = new ObjectProperties(ObjectProperties.WILD_CARD); ruleProperties.put(ObjectProperties.Property.COMPONENT, "Test"); rs.grant(1, "user5", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, ruleProperties); configureAccessControl(rs); @@ -235,6 +237,7 @@ public class DefaultAccessControlTest extends TestCase public void testAccess() throws Exception { final Subject subject = TestPrincipalUtils.createTestSubject("user1"); + final String testVirtualHost = getName(); final InetAddress inetAddress = InetAddress.getLocalHost(); final InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 1); @@ -250,13 +253,12 @@ public class DefaultAccessControlTest extends TestCase { RuleSet mockRuleSet = mock(RuleSet.class); - - DefaultAccessControl accessControl = new DefaultAccessControl(mockRuleSet); - accessControl.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + ObjectProperties properties = new ObjectProperties(testVirtualHost); + accessControl.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, properties); - verify(mockRuleSet).check(subject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY, inetAddress); + verify(mockRuleSet).check(subject, Operation.ACCESS, ObjectType.VIRTUALHOST, properties, inetAddress); return null; } }); 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 caf9b2fb61..32037807cd 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 @@ -21,24 +21,26 @@ package org.apache.qpid.server.security.access.plugins; -import java.security.Principal; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import javax.security.auth.Subject; -import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.Result; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.security.access.ObjectProperties.Property; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; import org.apache.qpid.server.security.auth.TestPrincipalUtils; import org.apache.qpid.test.utils.QpidTestCase; -import static org.mockito.Mockito.mock; - /** * This test checks that the {@link RuleSet} object which forms the core of the access control plugin performs correctly. * @@ -51,6 +53,9 @@ import static org.mockito.Mockito.mock; */ public class RuleSetTest extends QpidTestCase { + private static final String DENIED_VH = "deniedVH"; + private static final String ALLOWED_VH = "allowedVH"; + private RuleSet _ruleSet; // Object under test private static final String TEST_USER = "user"; @@ -60,6 +65,8 @@ public class RuleSetTest extends QpidTestCase private String _exchangeName = "amq.direct"; private String _exchangeType = "direct"; private Subject _testSubject = TestPrincipalUtils.createTestSubject(TEST_USER); + private AMQQueue _queue; + private VirtualHost _virtualHost; @Override public void setUp() throws Exception @@ -67,6 +74,11 @@ public class RuleSetTest extends QpidTestCase super.setUp(); _ruleSet = new RuleSet(mock(EventLoggerProvider.class)); + + _virtualHost = mock(VirtualHost.class); + _queue = mock(AMQQueue.class); + when(_queue.getName()).thenReturn(_queueName); + when(_queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); } @Override @@ -83,10 +95,8 @@ public class RuleSetTest extends QpidTestCase public void assertDenyGrantAllow(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties) { - final Principal identity = subject.getPrincipals().iterator().next(); - assertEquals(Result.DENIED, _ruleSet.check(subject, operation, objectType, properties)); - _ruleSet.grant(0, identity.getName(), Permission.ALLOW, operation, objectType, properties); + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, operation, objectType, properties); assertEquals(1, _ruleSet.getRuleCount()); assertEquals(Result.ALLOWED, _ruleSet.check(subject, operation, objectType, properties)); } @@ -98,17 +108,77 @@ public class RuleSetTest extends QpidTestCase assertEquals(_ruleSet.getDefault(), _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } - public void testVirtualHostAccess() throws Exception + public void testVirtualHostAccessAllowPermissionWithVirtualHostName() throws Exception { - assertDenyGrantAllow(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST); + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); } + public void testVirtualHostAccessAllowPermissionWithNameSetToWildCard() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ObjectProperties.WILD_CARD)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowPermissionWithNoName() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessDenyPermissionWithNoName() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessDenyPermissionWithNameSetToWildCard() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ObjectProperties.WILD_CARD)); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowDenyPermissions() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH)); + _ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowPermissionWithVirtualHostNameOtherPredicate() throws Exception + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.VIRTUALHOST_NAME, ALLOWED_VH); + + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, properties); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, properties)); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testQueueCreateNamed() throws Exception { assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queueName)); } - public void testQueueCreatenamedNullRoutingKey() + public void testQueueCreateNamedVirtualHost() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(Property.VIRTUALHOST_NAME, ALLOWED_VH)); + + when(_virtualHost.getName()).thenReturn(ALLOWED_VH); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queue))); + + when(_virtualHost.getName()).thenReturn(DENIED_VH); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queue))); + } + + public void testQueueCreateNamedNullRoutingKey() { ObjectProperties properties = new ObjectProperties(_queueName); properties.put(ObjectProperties.Property.ROUTING_KEY, (String) null); @@ -116,6 +186,21 @@ public class RuleSetTest extends QpidTestCase assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.QUEUE, properties); } + public void testExchangeCreateNamedVirtualHost() + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(Property.VIRTUALHOST_NAME, ALLOWED_VH)); + + ExchangeImpl exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getTypeName()).thenReturn(_exchangeType); + when(_virtualHost.getName()).thenReturn(ALLOWED_VH); + + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(exchange))); + + when(_virtualHost.getName()).thenReturn(DENIED_VH); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(exchange))); + } + public void testExchangeCreate() { ObjectProperties properties = new ObjectProperties(_exchangeName); diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java index 040be92ceb..999da2da6c 100644 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java @@ -291,7 +291,7 @@ public class ServerSessionDelegate extends SessionDelegate final VirtualHost virtualHost = getVirtualHost(ssn); try { - virtualHost.getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName()); + virtualHost.getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName(), virtualHost.getName()); } catch (AccessControlException e) { diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index baf5eceef7..7bde83cc99 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -303,9 +303,10 @@ public class AMQChannel> public void setPublishFrame(MessagePublishInfo info, final MessageDestination e) { String routingKey = info.getRoutingKey() == null ? null : info.getRoutingKey().asString(); - SecurityManager securityManager = getVirtualHost().getSecurityManager(); + VirtualHost virtualHost = getVirtualHost(); + SecurityManager securityManager = virtualHost.getSecurityManager(); - securityManager.authorisePublish(info.isImmediate(), routingKey, e.getName()); + securityManager.authorisePublish(info.isImmediate(), routingKey, e.getName(), virtualHost.getName()); _currentMessage = new IncomingMessage(info); _currentMessage.setMessageDestination(e); diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java index a29d56605a..1a29806f62 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java @@ -80,6 +80,8 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener vhost) { - SecurityManager securityManager = host.getSecurityManager(); - securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName); + SecurityManager securityManager = getBroker().getSecurityManager(); + securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName, vhost.getName()); } } diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java index 7b0a48cac1..5a7674d4fd 100644 --- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java @@ -60,15 +60,15 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler private MBeanServer _mbs; private final boolean _managementRightsInferAllAccess; - private final Broker _broker; + private final Broker _broker; - MBeanInvocationHandlerImpl(Broker broker) + MBeanInvocationHandlerImpl(Broker broker) { _managementRightsInferAllAccess = Boolean.valueOf(System.getProperty(BrokerProperties.PROPERTY_MANAGEMENT_RIGHTS_INFER_ALL_ACCESS, "true")); _broker = broker; } - public static MBeanServerForwarder newProxyInstance(Broker broker) + public static MBeanServerForwarder newProxyInstance(Broker broker) { final InvocationHandler handler = new MBeanInvocationHandlerImpl(broker); final Class[] interfaces = new Class[] { MBeanServerForwarder.class }; @@ -195,28 +195,23 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler String methodName; // Get the component, type and impact, which may be null String type = getType(method, args); - String vhost = getVirtualHost(method, args); + String virtualHostName = getVirtualHost(method, args); int impact = getImpact(method, args); - // Get the security manager for the virtual host (if set) - SecurityManager security; - if (vhost == null) + if (virtualHostName != null) { - security = _broker.getSecurityManager(); - } - else - { - VirtualHost virtualHost = _broker.findVirtualHostByName(vhost); + VirtualHost virtualHost = _broker.findVirtualHostByName(virtualHostName); if (virtualHost == null) { - throw new IllegalArgumentException("Virtual host with name '" + vhost + "' is not found."); + throw new IllegalArgumentException("Virtual host with name '" + virtualHostName + "' is not found."); } - security = virtualHost.getSecurityManager(); } methodName = getMethodName(method, args); Operation operation = (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO) ? Operation.ACCESS : Operation.UPDATE; - security.authoriseMethod(operation, type, methodName); + + SecurityManager security = _broker.getSecurityManager(); + security.authoriseMethod(operation, type, methodName, virtualHostName); if (_managementRightsInferAllAccess) { 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 37f960a65a..a0188626ee 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 @@ -49,8 +49,7 @@ public class AccessControlLoggingTest extends AbstractTestLogging public void setUp() throws Exception { // Write out ACL for this test - AbstractACLTestCase.writeACLFileUtil(this, "test", - "ACL ALLOW client ACCESS VIRTUALHOST", + AbstractACLTestCase.writeACLFileUtil(this, "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'", 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 7a3edd316f..461670dc1e 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 @@ -26,7 +26,6 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionURL; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.apache.qpid.url.URLSyntaxException; @@ -45,12 +44,10 @@ import java.util.concurrent.TimeUnit; /** * Abstract test case for ACLs. - * + * * This base class contains convenience methods to manage ACL files and implements a mechanism that allows each * test method to run its own setup code before the broker starts. - * - * TODO move the pre broker-startup setup method invocation code to {@link QpidBrokerTestCase} - * + * * @see ExternalACLTest * @see ExternalACLJMXTest * @see ExhaustiveACLTest @@ -80,7 +77,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements { throw (Exception) e.getTargetException(); } - + super.setUp(); } @@ -97,25 +94,18 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements //that we provoked with authentication failures, where the test passes - we can ignore on con close } } - - public void writeACLFile(final String vhost, final String...rules) throws ConfigurationException, IOException + + public void writeACLFile(final String...rules) throws ConfigurationException, IOException { - writeACLFileUtil(this, vhost, rules); + writeACLFileUtil(this, rules); } - public static void writeACLFileUtil(QpidBrokerTestCase testcase, String vhost, String...rules) throws ConfigurationException, IOException + public static void writeACLFileUtil(QpidBrokerTestCase testcase, String...rules) throws ConfigurationException, IOException { File aclFile = File.createTempFile(testcase.getClass().getSimpleName(), testcase.getName()); aclFile.deleteOnExit(); - if (vhost == null) - { - testcase.getBrokerConfiguration().addAclFileConfiguration(aclFile.getAbsolutePath()); - } - else - { - testcase.setVirtualHostConfigurationProperty("virtualhosts.virtualhost." + vhost + ".security.acl", aclFile.getAbsolutePath()); - } + testcase.getBrokerConfiguration().addAclFileConfiguration(aclFile.getAbsolutePath()); PrintWriter out = new PrintWriter(new FileWriter(aclFile)); out.println(String.format("# %s", testcase.getName())); @@ -127,7 +117,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements } /** - * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener + * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener * with a {@link CountDownLatch} to synchronise in the {@link #check403Exception(Throwable)} method and allow the * {@link #tearDown()} method to complete properly. */ @@ -137,8 +127,8 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements //Prevent Failover connection.setConnectionListener(this); - - //QPID-2081: use a latch to sync on exception causing connection close, to work + + //QPID-2081: use a latch to sync on exception causing connection close, to work //around the connection close race during tearDown() causing sporadic failures _exceptionReceived = new CountDownLatch(1); @@ -195,8 +185,8 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements assertNotNull("There was no linked exception", t); 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 + + //use the latch to ensure the control thread waits long enough for the exception thread //to have done enough to mark the connection closed before teardown commences assertTrue("Timed out waiting for conneciton to report close", _exceptionReceived.await(2, TimeUnit.SECONDS)); } 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 50c80692dd..07fe88b38f 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 @@ -39,20 +39,20 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Creates a queue. - * + * * Connects to the broker as a particular user and create the named queue on a virtual host, with the provided * parameters. Uses a new {@link Connection} and {@link Session} and closes them afterwards. */ private void createQueue(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception { - Connection conn = getConnection(vhost, user, "guest"); + Connection conn = getConnection(vhost, user, "guest"); Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); ((AMQSession) sess).createQueue(new AMQShortString(name), autoDelete, durable, false); sess.commit(); conn.close(); } - + /** * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that * no exceptions were thrown. @@ -61,7 +61,7 @@ public class ExhaustiveACLTest extends AbstractACLTestCase { try { - createQueue(vhost, user, name, autoDelete, durable); + createQueue(vhost, user, name, autoDelete, durable); } catch (AMQException e) { @@ -72,7 +72,7 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that - * the exception thrown was an {@link AMQConstant#ACCESS_REFUSED} or 403 error code. + * the exception thrown was an {@link AMQConstant#ACCESS_REFUSED} or 403 error code. */ private void createQueueFailure(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception { @@ -87,67 +87,64 @@ public class ExhaustiveACLTest extends AbstractACLTestCase assertEquals("Should be an ACCESS_REFUSED error", 403, e.getErrorCode().getCode()); } } - + public void setUpAuthoriseCreateQueueAutodelete() throws Exception { - writeACLFile("test", - "acl allow client access virtualhost", + writeACLFile("acl allow client access virtualhost", "acl allow server access virtualhost", "acl allow client create queue name=\"temp.true.*\" autodelete=true", "acl allow client create queue name=\"temp.false.*\" autodelete=false", - "acl deny client create queue", - "acl allow client delete queue", + "acl deny client create queue", + "acl allow client delete queue", "acl deny all create queue" ); } - + /** * Test creation of temporary queues, with the autodelete property set to true. */ public void testAuthoriseCreateQueueAutodelete() throws Exception { - createQueueSuccess("test", "client", "temp.true.00", true, false); + createQueueSuccess("test", "client", "temp.true.00", true, false); createQueueSuccess("test", "client", "temp.true.01", true, false); createQueueSuccess("test", "client", "temp.true.02", true, true); - createQueueSuccess("test", "client", "temp.false.03", false, false); + createQueueSuccess("test", "client", "temp.false.03", false, false); createQueueSuccess("test", "client", "temp.false.04", false, false); createQueueSuccess("test", "client", "temp.false.05", false, true); - createQueueFailure("test", "client", "temp.true.06", false, false); + createQueueFailure("test", "client", "temp.true.06", false, false); createQueueFailure("test", "client", "temp.false.07", true, false); - createQueueFailure("test", "server", "temp.true.08", true, false); + createQueueFailure("test", "server", "temp.true.08", true, false); createQueueFailure("test", "client", "temp.other.09", false, false); } - + public void setUpAuthoriseCreateQueue() throws Exception { - writeACLFile("test", - "acl allow client access virtualhost", + writeACLFile("acl allow client access virtualhost", "acl allow server access virtualhost", "acl allow client create queue name=\"create.*\"" ); } - + /** * Tests creation of named queues. * - * If a named queue is specified + * If a named queue is specified */ public void testAuthoriseCreateQueue() throws Exception { createQueueSuccess("test", "client", "create.00", true, true); createQueueSuccess("test", "client", "create.01", true, false); createQueueSuccess("test", "client", "create.02", false, true); - createQueueSuccess("test", "client", "create.03", true, false); + createQueueSuccess("test", "client", "create.03", true, false); createQueueFailure("test", "server", "create.04", true, true); createQueueFailure("test", "server", "create.05", true, false); createQueueFailure("test", "server", "create.06", false, true); - createQueueFailure("test", "server", "create.07", true, false); + createQueueFailure("test", "server", "create.07", true, false); } - + public void setUpAuthoriseCreateQueueBoth() throws Exception { - writeACLFile("test", - "acl allow all access virtualhost", + writeACLFile("acl allow all access virtualhost", "acl allow client create queue name=\"create.*\"", "acl allow all create queue temporary=true" ); @@ -156,16 +153,16 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Tests creation of named queues. * - * If a named queue is specified + * If a named queue is specified */ public void testAuthoriseCreateQueueBoth() throws Exception { createQueueSuccess("test", "client", "create.00", true, false); createQueueSuccess("test", "client", "create.01", false, false); createQueueFailure("test", "server", "create.02", false, false); - createQueueFailure("test", "guest", "create.03", false, false); + createQueueFailure("test", "guest", "create.03", false, false); createQueueSuccess("test", "client", "tmp.00", true, false); - createQueueSuccess("test", "server", "tmp.01", true, false); + createQueueSuccess("test", "server", "tmp.01", true, false); createQueueSuccess("test", "guest", "tmp.02", true, false); } } 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 389ac5ff4d..7a1a6cd639 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 @@ -56,7 +56,7 @@ public class ExternalACLJMXTest extends AbstractACLTestCase super.setUp(); _jmx.open(); } - + @Override public void tearDown() throws Exception { @@ -66,9 +66,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase public void setUpDenyAllIsCatchAllRule() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "#No more rules, default catch all (deny all) should apply"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "#No more rules, default catch all (deny all) should apply"); } public void testDenyAllIsCatchAllRule() throws Exception @@ -107,7 +106,7 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpAllowAll() throws Exception { - writeACLFile(null, "ACL ALLOW ALL ALL"); + writeACLFile("ACL ALLOW ALL ALL"); } public void testAllowAll() throws Exception @@ -118,24 +117,17 @@ public class ExternalACLJMXTest extends AbstractACLTestCase // PASS } - /** - * admin user is denied at broker level but allowed at vhost level. - */ - public void setUpVhostAllowOverridesGlobalDeny() throws Exception + public void setUpVhostWithName() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); - writeACLFile(TEST_VHOST, - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue' virtualhost_name='"+ TEST_VHOST + "'", + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue' virtualhost_name='"+ TEST2_VHOST + "'"); } - public void testVhostAllowOverridesGlobalDeny() throws Exception + public void testVhostWithName() 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.createQueue(TEST2_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); @@ -153,9 +145,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpUpdateComponentOnlyAllow() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager'"); } public void testUpdateComponentOnlyAllow() throws Exception @@ -172,9 +163,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpUpdateMethodOnlyAllow() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD"); } public void testUpdateMethodOnlyAllow() throws Exception @@ -187,12 +177,12 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user has JMX right, AMPQ right is irrelevant. + * admin user has JMX right, AMQP right is irrelevant. */ public void setUpCreateQueueSuccess() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); } public void testCreateQueueSuccess() throws Exception @@ -202,14 +192,13 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user has JMX right, verifies lack of AMPQ rights is irrelevant. + * admin user has JMX right, verifies lack of AMQP rights is irrelevant. */ public void setUpCreateQueueSuccessNoAMQPRights() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'", - "ACL DENY admin CREATE QUEUE"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'", + "ACL DENY admin CREATE QUEUE"); } public void testCreateQueueSuccessNoAMQPRights() throws Exception @@ -219,13 +208,12 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user does not have JMX right, AMPQ right is irrelevant. + * admin user does not have JMX right, AMQP right is irrelevant. */ public void setUpCreateQueueDenied() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, - "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); } public void testCreateQueueDenied() throws Exception @@ -247,9 +235,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationUpdateDenied() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL DENY admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL DENY admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); } public void testServerInformationUpdateDenied() throws Exception @@ -272,9 +259,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationAccessGranted() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW-LOG admin ACCESS METHOD component='ServerInformation' name='getManagementApiMajorVersion'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW-LOG admin ACCESS METHOD component='ServerInformation' name='getManagementApiMajorVersion'"); } public void testServerInformationAccessGranted() throws Exception @@ -299,9 +285,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationUpdateMethodPermission() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); } public void testServerInformationUpdateMethodPermission() throws Exception @@ -317,9 +302,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationAllMethodPermissions() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin ALL METHOD component='ServerInformation'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin ALL METHOD component='ServerInformation'"); } public void testServerInformationAllMethodPermissions() throws Exception 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 00c85e80c8..0e8f3cb7d8 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,12 +18,6 @@ */ package org.apache.qpid.server.security.acl; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.url.URLSyntaxException; - import javax.jms.Connection; import javax.jms.Destination; import javax.jms.JMSException; @@ -37,8 +31,14 @@ import javax.jms.Topic; import javax.jms.TopicSubscriber; 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.protocol.AMQConstant; +import org.apache.qpid.url.URLSyntaxException; + /** - * Tests the V2 ACLs. The tests perform basic AMQP operations like creating queues or excahnges and publishing and consuming messages, using + * Tests the V2 ACLs. The tests perform basic AMQP operations like creating queues or exchanges and publishing and consuming messages, using * JMS to contact the broker. */ public class ExternalACLTest extends AbstractACLTestCase @@ -46,65 +46,113 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpAccessAuthorizedSuccess() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST"); } public void testAccessAuthorizedSuccess() throws Exception + { + Connection conn = getConnection("test", "client", "guest"); + conn.close(); + } + + public void setUpAccessNoRightsFailure() throws Exception + { + writeACLFile("ACL DENY-LOG client ACCESS VIRTUALHOST"); + } + + public void testAccessNoRightsFailure() throws Exception { try { - Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); + getConnection("test", "client", "guest"); + fail("Connection was created."); + } + catch (JMSException e) + { + assertAccessDeniedException(e); + } + } - //Do something to show connection is active. - sess.rollback(); + private void assertAccessDeniedException(JMSException e) + { + assertEquals("Unexpected exception message", "Error creating connection: Permission denied: test", e.getMessage()); + + // JMSException -> linkedException -> cause = AMQException (403 or 320) + Exception linkedException = e.getLinkedException(); + assertNotNull("There was no linked exception", linkedException); + Throwable cause = linkedException.getCause(); + assertNotNull("Cause was null", cause); + assertTrue("Wrong linked exception type", cause instanceof AMQException); + AMQConstant errorCode = isBroker010() ? AMQConstant.CONNECTION_FORCED : AMQConstant.ACCESS_REFUSED; + assertEquals("Incorrect error code received", errorCode, ((AMQException) cause).getErrorCode()); + } + + public void setUpAccessVirtualHostWithName() throws Exception + { + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST name='test'", "ACL DENY-LOG guest ACCESS VIRTUALHOST name='test'", + "ACL ALLOW-LOG server ACCESS VIRTUALHOST name='*'"); + } - conn.close(); + public void testAccessVirtualHostWithName() throws Exception + { + Connection conn = getConnection("test", "client", "guest"); + conn.close(); + + try + { + getConnection("test", "guest", "guest"); + fail("Access should be denied"); } - catch (Exception e) + catch (JMSException e) { - fail("Connection was not created due to:" + e); + assertAccessDeniedException(e); } + + Connection conn2 = getConnection("test", "server", "guest"); + conn2.close(); } - public void setUpAccessNoRightsFailure() throws Exception + public void setUpClientCreateVirtualHostQueue() throws Exception { - writeACLFile("test", "ACL DENY-LOG client ACCESS VIRTUALHOST"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE virtualhost_name='test'", + "ACL ALLOW-LOG client CONSUME QUEUE", + "ACL ALLOW-LOG client BIND EXCHANGE", + "ACL ALLOW-LOG guest ACCESS VIRTUALHOST", + "ACL DENY-LOG guest CREATE QUEUE virtualhost_name='test'"); } - public void testAccessNoRightsFailure() throws Exception + public void testClientCreateVirtualHostQueue() throws NamingException, JMSException, AMQException, Exception { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination dest = sess.createQueue(getTestQueueName()); + sess.createConsumer(dest); + conn.close(); + try { - Connection conn = getConnection("test", "guest", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); - sess.rollback(); + conn = getConnection("test", "guest", "guest"); + sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + sess.createConsumer(dest); - fail("Connection was created."); + fail("Queue creation for user 'guest' is denied"); } catch (JMSException e) { - // JMSException -> linkedException -> cause = AMQException (403 or 320) - Exception linkedException = e.getLinkedException(); - assertNotNull("There was no linked exception", linkedException); - Throwable cause = linkedException.getCause(); - assertNotNull("Cause was null", cause); - assertTrue("Wrong linked exception type", cause instanceof AMQException); - AMQConstant errorCode = isBroker010() ? AMQConstant.CONNECTION_FORCED : AMQConstant.ACCESS_REFUSED; - assertEquals("Incorrect error code received", errorCode, ((AMQException) cause).getErrorCode()); + check403Exception(e.getLinkedException()); } } + public void setUpClientDeleteQueueSuccess() 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 ALLOW-LOG client DELETE QUEUE durable=\"true\"", - "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); + writeACLFile("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 testClientDeleteQueueSuccess() throws Exception @@ -128,12 +176,12 @@ public class ExternalACLTest extends AbstractACLTestCase 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"); + writeACLFile("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 testClientDeleteQueueFailure() throws Exception @@ -177,10 +225,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientConsumeFromNamedQueueValid() throws Exception { - 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\""); + writeACLFile("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\""); } @@ -197,10 +245,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientConsumeFromNamedQueueFailure() throws Exception { - 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\""); + writeACLFile("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 testClientConsumeFromNamedQueueFailure() throws NamingException, Exception @@ -224,11 +272,11 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateTemporaryQueueSuccess() throws Exception { - 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"); + writeACLFile("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"); } public void testClientCreateTemporaryQueueSuccess() throws JMSException, URLSyntaxException, Exception @@ -243,8 +291,8 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateTemporaryQueueFailed() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL DENY-LOG client CREATE QUEUE temporary=\"true\""); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL DENY-LOG client CREATE QUEUE temporary=\"true\""); } public void testClientCreateTemporaryQueueFailed() throws NamingException, Exception @@ -268,9 +316,8 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateNamedQueueFailure() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE name=\"ValidQueue\"", - "ACL ALLOW-LOG client CONSUME QUEUE"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE name=\"ValidQueue\""); } public void testClientCreateNamedQueueFailure() throws NamingException, JMSException, AMQException, Exception @@ -294,10 +341,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientPublishUsingTransactionSuccess() throws Exception { - 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\""); + writeACLFile("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 testClientPublishUsingTransactionSuccess() throws Exception @@ -329,19 +376,18 @@ public class ExternalACLTest extends AbstractACLTestCase // We tolerate a dependency from this test to that file because its // contents are expected to change rarely. - writeACLFile("test", "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"); + writeACLFile("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"); } @@ -386,9 +432,9 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client ALL QUEUE", - "ACL ALLOW-LOG client ALL EXCHANGE"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client ALL QUEUE", + "ACL ALLOW-LOG client ALL EXCHANGE"); } public void testClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception @@ -412,7 +458,7 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpFirewallAllow() throws Exception { - writeACLFile("test", "ACL ALLOW client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); + writeACLFile("ACL ALLOW client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); } public void testFirewallAllow() throws Exception @@ -423,7 +469,7 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpFirewallDeny() throws Exception { - writeACLFile("test", "ACL DENY client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); + writeACLFile("ACL DENY client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); } public void testFirewallDeny() throws Exception diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java index 7f41a8eb2e..2d88d02ee5 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java @@ -63,8 +63,7 @@ public class BrokerACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER", "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER", "ACL DENY-LOG ALL ALL"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java index b39d994198..3b9027fb97 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java @@ -46,8 +46,7 @@ public class ExchangeRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE EXCHANGE", "ACL DENY-LOG " + DENIED_USER + " CREATE EXCHANGE", "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE EXCHANGE", diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java index 9a578d01fb..02d6e48d81 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -95,8 +95,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testCreateGroup() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " CREATE GROUP"); @@ -122,8 +121,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testDeleteGroup() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " DELETE GROUP"); @@ -149,8 +147,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testUpdateGroupAddMember() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); @@ -170,8 +167,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testUpdateGroupDeleteMember() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java index 96ea5c92b3..8dd3a52333 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java @@ -39,8 +39,7 @@ public class LogViewerACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS_LOGS BROKER", "ACL DENY-LOG " + DENIED_USER + " ACCESS_LOGS BROKER", "ACL DENY-LOG ALL ALL"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java index b187ca955a..07206a1379 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java @@ -43,8 +43,7 @@ public class QueueRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", "ACL DENY-LOG " + DENIED_USER + " CREATE QUEUE", "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE QUEUE", diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java index 6ed84ac95a..efde7d51b7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java @@ -77,7 +77,7 @@ public class UserPreferencesRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE USER", "ACL DENY-LOG " + DENIED_USER + " UPDATE USER", "ACL DENY-LOG ALL ALL"); TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java index 4c4e219695..b4f4d0b4dd 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -97,8 +97,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testAddUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE USER", "ACL DENY-LOG " + DENIED_GROUP + " CREATE USER"); @@ -122,8 +121,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testDeleteUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE USER", "ACL DENY-LOG " + DENIED_GROUP + " DELETE USER"); @@ -143,8 +141,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testUpdateUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE USER", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE USER"); -- cgit v1.2.1 From eeba35b5cda54b962b7b1e10659418c12c3ba324 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Fri, 21 Mar 2014 23:08:42 +0000 Subject: QPID-5639 : [Java Broker] Add SCRAM-SHA-1 SASL support git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1580082 13f79535-47bb-0310-9956-ffa450edef68 --- .../startup/AuthenticationProviderRecoverer.java | 22 +- .../qpid/server/model/AuthenticationProvider.java | 2 + .../manager/AbstractAuthenticationManager.java | 8 +- .../manager/ScramSHA1AuthenticationManager.java | 696 +++++++++++++++++++++ .../ScramSHA1AuthenticationManagerFactory.java | 74 +++ .../auth/sasl/scram/ScramSHA1SaslServer.java | 273 ++++++++ ...qpid.server.plugin.AuthenticationManagerFactory | 2 + .../ScramSHA1AuthenticationManagerTest.java | 203 ++++++ qpid/java/broker-plugins/management-http/pom.xml | 6 + .../management/plugin/servlet/FileServlet.java | 4 + .../plugin/servlet/rest/SaslServlet.java | 12 +- .../java/resources/js/qpid/authorization/sasl.js | 221 +++++-- .../src/main/java/resources/js/qpid/common/util.js | 2 +- qpid/java/build.deps | 4 +- .../security/CallbackHandlerRegistry.properties | 2 + .../security/DynamicSaslRegistrar.properties | 1 + qpid/java/ivy.retrieve.xml | 1 + 17 files changed, 1489 insertions(+), 44 deletions(-) create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManager.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java create mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java index 8eec88d556..46f3cd458b 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.User; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; public class AuthenticationProviderRecoverer implements ConfiguredObjectRecoverer @@ -68,9 +69,24 @@ public class AuthenticationProviderRecoverer implements ConfiguredObjectRecovere Map> childEntries, String type) { - ConfiguredObjectRecoverer recoverer = recovererProvider.getRecoverer(type); + ConfiguredObjectRecoverer recoverer = null; + + if(authenticationProvider instanceof RecovererProvider) + { + recoverer = ((RecovererProvider)authenticationProvider).getRecoverer(type); + } + + if(recoverer == null) + { + recoverer = recovererProvider.getRecoverer(type); + } + if (recoverer == null) { + if(authenticationProvider instanceof RecovererProvider) + { + ((RecovererProvider)authenticationProvider).getRecoverer(type); + } throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); } Collection entries = childEntries.get(type); @@ -85,6 +101,10 @@ public class AuthenticationProviderRecoverer implements ConfiguredObjectRecovere { authenticationProvider.setPreferencesProvider((PreferencesProvider)object); } + else if(object instanceof User) + { + authenticationProvider.recoverUser((User)object); + } else { throw new IllegalConfigurationException("Cannot associate " + object + " with authentication provider " + authenticationProvider); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java index 8e1ea39cec..fc0a8ab7e5 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java @@ -55,4 +55,6 @@ public interface AuthenticationProvider> ext * @param preferencesProvider */ void setPreferencesProvider(PreferencesProvider preferencesProvider); + + void recoverUser(User user); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java index 7c521c1f8a..f15195b812 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.security.auth.manager; import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.model.*; import org.apache.qpid.server.model.adapter.AbstractConfiguredObject; @@ -85,8 +86,11 @@ public abstract class AbstractAuthenticationManager + implements PasswordCredentialManagingAuthenticationProvider, + RecovererProvider +{ + public static final String SCRAM_USER_TYPE = "scram"; + private static final Charset ASCII = Charset.forName("ASCII"); + public static final String HMAC_SHA_1 = "HmacSHA1"; + private final SecureRandom _random = new SecureRandom(); + private int _iterationCount = 4096; + private Map _users = new ConcurrentHashMap(); + + + protected ScramSHA1AuthenticationManager(final Broker broker, + final Map defaults, + final Map attributes, + final boolean recovering) + { + super(broker, defaults, attributes); + } + + @Override + public void initialise() + { + + } + + @Override + public String getMechanisms() + { + return ScramSHA1SaslServer.MECHANISM; + } + + @Override + public SaslServer createSaslServer(final String mechanism, + final String localFQDN, + final Principal externalPrincipal) + throws SaslException + { + return new ScramSHA1SaslServer(this); + } + + @Override + public AuthenticationResult authenticate(final SaslServer server, final byte[] response) + { + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete()) + { + final String userId = server.getAuthorizationID(); + return new AuthenticationResult(new UsernamePrincipal(userId)); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + @Override + public AuthenticationResult authenticate(final String username, final String password) + { + ScramAuthUser user = getUser(username); + if(user != null) + { + final String[] usernamePassword = user.getPassword().split(","); + byte[] salt = DatatypeConverter.parseBase64Binary(usernamePassword[0]); + try + { + if(Arrays.equals(DatatypeConverter.parseBase64Binary(usernamePassword[1]),createSaltedPassword(salt, password))) + { + return new AuthenticationResult(new UsernamePrincipal(username)); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR,e); + } + + } + + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + + + } + + @Override + public void delete() + { + + } + + @Override + public void close() + { + + } + + public int getIterationCount() + { + return _iterationCount; + } + + public byte[] getSalt(final String username) + { + ScramAuthUser user = getUser(username); + + if(user == null) + { + // don't disclose that the user doesn't exist, just generate random data so the failure is indistinguishable + // from the "wrong password" case + + byte[] salt = new byte[32]; + _random.nextBytes(salt); + return salt; + } + else + { + return DatatypeConverter.parseBase64Binary(user.getPassword().split(",")[0]); + } + } + + private static final byte[] INT_1 = new byte[]{0, 0, 0, 1}; + + public byte[] getSaltedPassword(final String username) throws SaslException + { + ScramAuthUser user = getUser(username); + if(user == null) + { + throw new SaslException("Authentication Failed"); + } + else + { + return DatatypeConverter.parseBase64Binary(user.getPassword().split(",")[1]); + } + } + + private ScramAuthUser getUser(final String username) + { + return _users.get(username); + } + + private byte[] createSaltedPassword(byte[] salt, String password) throws SaslException + { + Mac mac = createSha1Hmac(password.getBytes(ASCII)); + + mac.update(salt); + mac.update(INT_1); + byte[] result = mac.doFinal(); + + byte[] previous = null; + for(int i = 1; i < getIterationCount(); i++) + { + mac.update(previous != null? previous: result); + previous = mac.doFinal(); + for(int x = 0; x < result.length; x++) + { + result[x] ^= previous[x]; + } + } + + return result; + + } + + private Mac createSha1Hmac(final byte[] keyBytes) + throws SaslException + { + try + { + SecretKeySpec key = new SecretKeySpec(keyBytes, HMAC_SHA_1); + Mac mac = Mac.getInstance(HMAC_SHA_1); + mac.init(key); + return mac; + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new SaslException(e.getMessage(), e); + } + } + + @Override + public boolean createUser(final String username, final String password, final Map attributes) + { + if (getTaskExecutor().isTaskExecutorThread()) + { + getSecurityManager().authoriseUserOperation(Operation.CREATE, username); + if(_users.containsKey(username)) + { + throw new IllegalArgumentException("User '"+username+"' already exists"); + } + try + { + Map userAttrs = new HashMap(); + userAttrs.put(User.ID, UUID.randomUUID()); + userAttrs.put(User.NAME, username); + userAttrs.put(User.PASSWORD, createStoredPassword(password)); + userAttrs.put(User.TYPE, SCRAM_USER_TYPE); + ScramAuthUser user = new ScramAuthUser(userAttrs); + _users.put(username, user); + + return true; + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + else + { + return getTaskExecutor().submitAndWait(new TaskExecutor.Task() + { + @Override + public Boolean call() + { + return createUser(username, password, attributes); + } + }); + } + + } + + private SecurityManager getSecurityManager() + { + return getBroker().getSecurityManager(); + } + + @Override + public void deleteUser(final String user) throws AccountNotFoundException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + + final ScramAuthUser authUser = getUser(user); + if(authUser != null) + { + authUser.setState(State.ACTIVE, State.DELETED); + } + else + { + throw new AccountNotFoundException("No such user: '" + user + "'"); + } + } + else + { + AccountNotFoundException e = + getTaskExecutor().submitAndWait(new TaskExecutor.Task() { + + @Override + public AccountNotFoundException call() + { + try + { + deleteUser(user); + return null; + } + catch (AccountNotFoundException e) + { + return e; + } + + } + }); + + if(e != null) + { + throw e; + } + } + } + + @Override + public void setPassword(final String username, final String password) throws AccountNotFoundException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + final ScramAuthUser authUser = getUser(username); + if(authUser != null) + { + authUser.setPassword(password); + } + else + { + throw new AccountNotFoundException("No such user: '" + username + "'"); + } + } + else + { + AccountNotFoundException e = + getTaskExecutor().submitAndWait(new TaskExecutor.Task() + { + + @Override + public AccountNotFoundException call() + { + try + { + setPassword(username, password); + return null; + } + catch (AccountNotFoundException e) + { + return e; + } + + } + }); + + if (e != null) + { + throw e; + } + } + + } + + @Override + public Map> getUsers() + { + if (getTaskExecutor().isTaskExecutorThread()) + { + Map> users = new HashMap>(); + for(String user : _users.keySet()) + { + users.put(user, Collections.emptyMap()); + } + return users; + } + else + { + return getTaskExecutor().submitAndWait(new TaskExecutor.Task>>() + { + @Override + public Map> call() + { + return getUsers(); + } + }); + } + } + + @Override + public void reload() throws IOException + { + + } + + private static Map, ConfiguredObject> parentsMap(final ScramSHA1AuthenticationManager scramSHA1AuthenticationManager) + { + + final Map, ConfiguredObject> map = new HashMap, ConfiguredObject>(); + map.put(AuthenticationProvider.class, scramSHA1AuthenticationManager); + return map; + } + + @Override + public ConfiguredObjectRecoverer getRecoverer(final String type) + { + if("User".equals(type)) + { + return new UserRecoverer(); + } + else + { + return null; + } + } + + private class ScramAuthUser extends AbstractConfiguredObject implements User + { + + + protected ScramAuthUser(final Map attributes) + { + super(parentsMap(ScramSHA1AuthenticationManager.this), + Collections.emptyMap(), + attributes, ScramSHA1AuthenticationManager.this.getTaskExecutor()); + + if(!ASCII.newEncoder().canEncode(getName())) + { + throw new IllegalArgumentException("Scram SHA1 user names are restricted to characters in the ASCII charset"); + } + } + + @Override + protected boolean setState(final State currentState, final State desiredState) + { + if(desiredState == State.DELETED) + { + getSecurityManager().authoriseUserOperation(Operation.DELETE, getName()); + _users.remove(getName()); + ScramSHA1AuthenticationManager.this.childRemoved(this); + return true; + } + else + { + return false; + } + } + + @Override + public void setAttributes(final Map attributes) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + Map modifiedAttributes = new HashMap(attributes); + final String newPassword = (String) attributes.get(User.PASSWORD); + if(attributes.containsKey(User.PASSWORD) && !newPassword.equals(getActualAttributes().get(User.PASSWORD))) + { + try + { + modifiedAttributes.put(User.PASSWORD, createStoredPassword(newPassword)); + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + super.setAttributes(modifiedAttributes); + } + else + { + getTaskExecutor().submitAndWait(new ChangeAttributesTask(this, attributes)); + } + + } + + @Override + public Object getAttribute(final String name) + { + if(PASSWORD.equals(name)) + { + return null; // for security reasons we don't expose the password + } + return super.getAttribute(name); + } + + @Override + public String getPassword() + { + return (String) getActualAttributes().get(PASSWORD); + } + + @Override + public void setPassword(final String password) + { + getSecurityManager().authoriseUserOperation(Operation.UPDATE, getName()); + + try + { + changeAttribute(User.PASSWORD, getAttribute(User.PASSWORD), createStoredPassword(password)); + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + + @Override + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + throw new IllegalStateException("Names cannot be updated"); + } + + @Override + public State getState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if(expected == desired && expected == LifetimePolicy.PERMANENT) + { + return LifetimePolicy.PERMANENT; + } + throw new IllegalArgumentException("Cannot change lifetime policy of a user"); + + } + + @Override + public Collection getChildren(final Class clazz) + { + return Collections.emptySet(); + } + + @Override + public Map getPreferences() + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return null; + } + return preferencesProvider.getPreferences(this.getName()); + } + + @Override + public Object getPreference(String name) + { + Map preferences = getPreferences(); + if (preferences == null) + { + return null; + } + return preferences.get(name); + } + + @Override + public Map setPreferences(Map preferences) + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return null; + } + return preferencesProvider.setPreferences(this.getName(), preferences); + } + + @Override + public boolean deletePreferences() + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return false; + } + String[] deleted = preferencesProvider.deletePreferences(this.getName()); + return deleted.length == 1; + } + + @Override + public Collection getAttributeNames() + { + return getAttributeNames(getClass()); + } + } + + @Override + public void recoverUser(final User user) + { + _users.put(user.getName(), (ScramAuthUser) user); + } + + protected String createStoredPassword(final String password) throws SaslException + { + byte[] salt = new byte[32]; + _random.nextBytes(salt); + byte[] passwordBytes = createSaltedPassword(salt, password); + return DatatypeConverter.printBase64Binary(salt) + "," + DatatypeConverter.printBase64Binary(passwordBytes); + } + + @Override + public C addChild(final Class childClass, + final Map attributes, + final ConfiguredObject... otherParents) + { + if(childClass == User.class) + { + String username = (String) attributes.get("name"); + String password = (String) attributes.get("password"); + Principal p = new UsernamePrincipal(username); + + if(createUser(username, password,null)) + { + @SuppressWarnings("unchecked") + C principalAdapter = (C) _users.get(username); + return principalAdapter; + } + else + { + return null; + + } + } + return super.addChild(childClass, attributes, otherParents); + } + + public Collection getChildren(Class clazz) + { + if(clazz == User.class) + { + return new ArrayList(_users.values()); + } + else + { + return super.getChildren(clazz); + } + } + + private class UserRecoverer implements ConfiguredObjectRecoverer + { + @Override + public ScramAuthUser create(final RecovererProvider recovererProvider, + final ConfigurationEntry entry, + final ConfiguredObject... parents) + { + + Map attributes = new HashMap(entry.getAttributes()); + attributes.put(User.ID,entry.getId()); + return new ScramAuthUser(attributes); + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java new file mode 100644 index 0000000000..dd6f77e474 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.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.server.security.auth.manager; + +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.util.ResourceBundleLoader; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public class ScramSHA1AuthenticationManagerFactory implements AuthenticationManagerFactory +{ + + public static final String PROVIDER_TYPE = "SCRAM-SHA1"; + + public static final String ATTRIBUTE_NAME = "name"; + + public static final Collection ATTRIBUTES = Collections. unmodifiableList(Arrays.asList( + AuthenticationProvider.TYPE + )); + + @Override + public ScramSHA1AuthenticationManager createInstance(Broker broker, + Map attributes, + final boolean recovering) + { + if (attributes == null || !PROVIDER_TYPE.equals(attributes.get(AuthenticationProvider.TYPE))) + { + return null; + } + + + return new ScramSHA1AuthenticationManager(broker, Collections.emptyMap(),attributes, false); + } + + @Override + public Collection getAttributeNames() + { + return ATTRIBUTES; + } + + @Override + public String getType() + { + return PROVIDER_TYPE; + } + + @Override + public Map getAttributeDescriptions() + { + return Collections.emptyMap(); + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java new file mode 100644 index 0000000000..71ef386e3e --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java @@ -0,0 +1,273 @@ +/* + * + * 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.auth.sasl.scram; + +import org.apache.qpid.server.security.auth.manager.ScramSHA1AuthenticationManager; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.xml.bind.DatatypeConverter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.UUID; + +public class ScramSHA1SaslServer implements SaslServer +{ + public static final String MECHANISM = "SCRAM-SHA-1"; + + private static final Charset ASCII = Charset.forName("ASCII"); + + private final ScramSHA1AuthenticationManager _authManager; + private State _state = State.INITIAL; + private String _nonce; + private String _username; + private byte[] _gs2Header; + private String _serverFirstMessage; + private String _clientFirstMessageBare; + private byte[] _serverSignature; + + public ScramSHA1SaslServer(final ScramSHA1AuthenticationManager authenticationManager) + { + _authManager = authenticationManager; + } + + enum State + { + INITIAL, + SERVER_FIRST_MESSAGE_SENT, + COMPLETE + } + + @Override + public String getMechanismName() + { + return MECHANISM; + } + + @Override + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + byte[] challenge; + switch (_state) + { + case INITIAL: + challenge = generateServerFirstMessage(response); + _state = State.SERVER_FIRST_MESSAGE_SENT; + break; + case SERVER_FIRST_MESSAGE_SENT: + challenge = generateServerFinalMessage(response); + _state = State.COMPLETE; + break; + default: + throw new SaslException("No response expected in state " + _state); + + } + return challenge; + } + + private byte[] generateServerFirstMessage(final byte[] response) throws SaslException + { + String clientFirstMessage = new String(response, ASCII); + if(!clientFirstMessage.startsWith("n")) + { + throw new SaslException("Cannot parse gs2-header"); + } + String[] parts = clientFirstMessage.split(","); + if(parts.length < 4) + { + throw new SaslException("Cannot parse client first message"); + } + _gs2Header = ("n,"+parts[1]+",").getBytes(ASCII); + _clientFirstMessageBare = clientFirstMessage.substring(_gs2Header.length); + if(!parts[2].startsWith("n=")) + { + throw new SaslException("Cannot parse client first message"); + } + _username = decodeUsername(parts[2].substring(2)); + if(!parts[3].startsWith("r=")) + { + throw new SaslException("Cannot parse client first message"); + } + _nonce = parts[3].substring(2) + UUID.randomUUID().toString(); + + int count = _authManager.getIterationCount(); + byte[] saltBytes = _authManager.getSalt(_username); + _serverFirstMessage = "r="+_nonce+",s="+ DatatypeConverter.printBase64Binary(saltBytes)+",i=" + count; + return _serverFirstMessage.getBytes(ASCII); + } + + private String decodeUsername(String username) throws SaslException + { + if(username.contains("=")) + { + String check = username; + while (check.contains("=")) + { + check = check.substring(check.indexOf('=') + 1); + if (!(check.startsWith("2C") || check.startsWith("3D"))) + { + throw new SaslException("Invalid username"); + } + } + username = username.replace("=2C", ","); + username = username.replace("=3D","="); + } + return username; + } + + + private byte[] generateServerFinalMessage(final byte[] response) throws SaslException + { + try + { + String clientFinalMessage = new String(response, ASCII); + String[] parts = clientFinalMessage.split(","); + if(!parts[0].startsWith("c=")) + { + throw new SaslException("Cannot parse client final message"); + } + if(!Arrays.equals(_gs2Header,DatatypeConverter.parseBase64Binary(parts[0].substring(2)))) + { + throw new SaslException("Client final message channel bind data invalid"); + } + if(!parts[1].startsWith("r=")) + { + throw new SaslException("Cannot parse client final message"); + } + if(!parts[1].substring(2).equals(_nonce)) + { + throw new SaslException("Client final message has incorrect nonce value"); + } + if(!parts[parts.length-1].startsWith("p=")) + { + throw new SaslException("Client final message does not have proof"); + } + + String clientFinalMessageWithoutProof = clientFinalMessage.substring(0,clientFinalMessage.length()-(1+parts[parts.length-1].length())); + byte[] proofBytes = DatatypeConverter.parseBase64Binary(parts[parts.length-1].substring(2)); + + String authMessage = _clientFirstMessageBare + "," + _serverFirstMessage + "," + clientFinalMessageWithoutProof; + + byte[] saltedPassword = _authManager.getSaltedPassword(_username); + + byte[] clientKey = computeHmacSHA1(saltedPassword, "Client Key"); + + byte[] storedKey = MessageDigest.getInstance("SHA1").digest(clientKey); + + byte[] clientSignature = computeHmacSHA1(storedKey, authMessage); + + byte[] clientProof = clientKey.clone(); + for(int i = 0 ; i < clientProof.length; i++) + { + clientProof[i] ^= clientSignature[i]; + } + + if(!Arrays.equals(clientProof, proofBytes)) + { + throw new SaslException("Authentication failed"); + } + byte[] serverKey = computeHmacSHA1(saltedPassword, "Server Key"); + String finalResponse = "v=" + DatatypeConverter.printBase64Binary(computeHmacSHA1(serverKey, authMessage)); + + return finalResponse.getBytes(ASCII); + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (UnsupportedEncodingException e) + { + throw new SaslException(e.getMessage(), e); + } + } + + @Override + public boolean isComplete() + { + return _state == State.COMPLETE; + } + + @Override + public String getAuthorizationID() + { + return _username; + } + + @Override + public byte[] unwrap(final byte[] incoming, final int offset, final int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + + @Override + public byte[] wrap(final byte[] outgoing, final int offset, final int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + + @Override + public Object getNegotiatedProperty(final String propName) + { + return null; + } + + @Override + public void dispose() throws SaslException + { + + } + + private byte[] computeHmacSHA1(final byte[] key, final String string) + throws SaslException, UnsupportedEncodingException + { + Mac mac = createSha1Hmac(key); + mac.update(string.getBytes(ASCII)); + return mac.doFinal(); + } + + + private Mac createSha1Hmac(final byte[] keyBytes) + throws SaslException + { + try + { + SecretKeySpec key = new SecretKeySpec(keyBytes, "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(key); + return mac; + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new SaslException(e.getMessage(), e); + } + } + +} diff --git a/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory b/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory index 8ff67030ef..a1139b386c 100644 --- a/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory +++ b/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory @@ -22,3 +22,5 @@ org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManagerFactor org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManagerFactory org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory org.apache.qpid.server.security.auth.manager.SimpleLDAPAuthenticationManagerFactory +org.apache.qpid.server.security.auth.manager.ScramSHA1AuthenticationManagerFactory + diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java new file mode 100644 index 0000000000..78b4b22ff3 --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java @@ -0,0 +1,203 @@ +/* + * + * 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.auth.manager; + +import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.test.utils.QpidTestCase; + import org.apache.qpid.server.security.SecurityManager; + +import javax.security.auth.login.AccountNotFoundException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ScramSHA1AuthenticationManagerTest extends QpidTestCase +{ + private ScramSHA1AuthenticationManager _authManager; + private Broker _broker; + private SecurityManager _securityManager; + private TaskExecutor _executor; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _executor = new TaskExecutor(); + _executor.start(); + _broker = mock(Broker.class); + _securityManager = mock(SecurityManager.class); + when(_broker.getTaskExecutor()).thenReturn(_executor); + when(_broker.getSecurityManager()).thenReturn(_securityManager); + final Map attributesMap = new HashMap(); + attributesMap.put(AuthenticationProvider.NAME, getTestName()); + attributesMap.put(AuthenticationProvider.ID, UUID.randomUUID()); + _authManager = new ScramSHA1AuthenticationManager(_broker, Collections.emptyMap(),attributesMap,false); + } + + @Override + public void tearDown() throws Exception + { + _executor.stop(); + super.tearDown(); + } + + public void testAddChildAndThenDelete() + { + // No children should be present before the test starts + assertEquals("No users should be present before the test starts", 0, _authManager.getChildren(User.class).size()); + assertEquals("No users should be present before the test starts", 0, _authManager.getUsers().size()); + + final Map childAttrs = new HashMap(); + + childAttrs.put(User.NAME, getTestName()); + childAttrs.put(User.PASSWORD, "password"); + User user = _authManager.addChild(User.class, childAttrs); + assertNotNull("User should be created but addChild returned null", user); + assertEquals(getTestName(), user.getName()); + // password shouldn't actually be the given string, but instead salt and the hashed value + assertFalse("Password shouldn't actually be the given string, but instead salt and the hashed value", "password".equals(user.getPassword())); + + AuthenticationResult authResult = + _authManager.authenticate(getTestName(), "password"); + + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + assertEquals("Manager should have exactly one user child",1, _authManager.getChildren(User.class).size()); + assertEquals("Manager should have exactly one user child",1, _authManager.getUsers().size()); + + + user.setDesiredState(State.ACTIVE, State.DELETED); + + assertEquals("No users should be present after child deletion", 0, _authManager.getChildren(User.class).size()); + + + authResult = _authManager.authenticate(getTestName(), "password"); + assertEquals("User should no longer authenticate with given password", AuthenticationResult.AuthenticationStatus.ERROR, authResult.getStatus()); + + } + + public void testCreateUser() + { + assertEquals("No users should be present before the test starts", 0, _authManager.getChildren(User.class).size()); + assertTrue(_authManager.createUser(getTestName(), "password", Collections.emptyMap())); + assertEquals("Manager should have exactly one user child",1, _authManager.getChildren(User.class).size()); + User user = _authManager.getChildren(User.class).iterator().next(); + assertEquals(getTestName(), user.getName()); + // password shouldn't actually be the given string, but instead salt and the hashed value + assertFalse("Password shouldn't actually be the given string, but instead salt and the hashed value", "password".equals(user.getPassword())); + final Map childAttrs = new HashMap(); + + childAttrs.put(User.NAME, getTestName()); + childAttrs.put(User.PASSWORD, "password"); + try + { + user = _authManager.addChild(User.class, childAttrs); + fail("Should not be able to create a second user with the same name"); + } + catch(IllegalArgumentException e) + { + // pass + } + try + { + _authManager.deleteUser(getTestName()); + } + catch (AccountNotFoundException e) + { + fail("AccountNotFoundException thrown when none was expected: " + e.getMessage()); + } + try + { + _authManager.deleteUser(getTestName()); + fail("AccountNotFoundException not thrown when was expected"); + } + catch (AccountNotFoundException e) + { + // pass + } + } + + public void testUpdateUser() + { + assertTrue(_authManager.createUser(getTestName(), "password", Collections.emptyMap())); + assertTrue(_authManager.createUser(getTestName()+"_2", "password", Collections.emptyMap())); + assertEquals("Manager should have exactly two user children",2, _authManager.getChildren(User.class).size()); + + AuthenticationResult authResult = _authManager.authenticate(getTestName(), "password"); + + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + authResult = _authManager.authenticate(getTestName()+"_2", "password"); + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + for(User user : _authManager.getChildren(User.class)) + { + if(user.getName().equals(getTestName())) + { + user.setAttributes(Collections.singletonMap(User.PASSWORD, "newpassword")); + } + } + + authResult = _authManager.authenticate(getTestName(), "newpassword"); + assertEquals("User should authenticate with updated password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + authResult = _authManager.authenticate(getTestName()+"_2", "password"); + assertEquals("User should authenticate with original password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + authResult = _authManager.authenticate(getTestName(), "password"); + assertEquals("User not authenticate with original password", AuthenticationResult.AuthenticationStatus.ERROR, authResult.getStatus()); + + for(User user : _authManager.getChildren(User.class)) + { + if(user.getName().equals(getTestName())) + { + user.setPassword("newerpassword"); + } + } + + authResult = _authManager.authenticate(getTestName(), "newerpassword"); + assertEquals("User should authenticate with updated password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + + + } + + public void testNonASCIIUser() + { + try + { + _authManager.createUser(getTestName()+"£", "password", Collections.emptyMap()); + fail("Expected exception when attempting to create a user with a non ascii name"); + } + catch(IllegalArgumentException e) + { + // pass + } + } + +} diff --git a/qpid/java/broker-plugins/management-http/pom.xml b/qpid/java/broker-plugins/management-http/pom.xml index 4bfaf5e5d2..afb295f7cd 100644 --- a/qpid/java/broker-plugins/management-http/pom.xml +++ b/qpid/java/broker-plugins/management-http/pom.xml @@ -69,6 +69,12 @@ zip + + org.webjars + cryptojs + 3.1.2 + + org.apache.qpid diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java index 618aaed319..72e9bac29b 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java @@ -89,6 +89,10 @@ public class FileServlet extends HttpServlet } URL resourceURL = getClass().getResource(_resourcePathPrefix + filename); + if(resourceURL == null) + { + resourceURL = getClass().getResource("/META-INF" + _resourcePathPrefix + filename); + } if(resourceURL != null) { response.setStatus(HttpServletResponse.SC_OK); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java index 2ca67fadc9..af3973c7b3 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -59,7 +59,7 @@ public class SaslServlet extends AbstractServlet private static final String ATTR_ID = "SaslServlet.ID"; private static final String ATTR_SASL_SERVER = "SaslServlet.SaslServer"; private static final String ATTR_EXPIRY = "SaslServlet.Expiry"; - private static final long SASL_EXCHANGE_EXPIRY = 1000L; + private static final long SASL_EXCHANGE_EXPIRY = 3000L; public SaslServlet() { @@ -260,7 +260,17 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); + if(challenge != null && challenge.length != 0) + { + Map outputObject = new LinkedHashMap(); + outputObject.put("challenge", new String(Base64.encodeBase64(challenge))); + + final PrintWriter writer = response.getWriter(); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); + mapper.writeValue(writer, outputObject); + } response.setStatus(HttpServletResponse.SC_OK); } else diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js index 2d99f886ed..a25375a669 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js @@ -18,8 +18,8 @@ * under the License. * */ -define(["dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5"], - function (xhr, base64, digestsBase, MD5) { +define(["dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5", "dojox/uuid/generateRandomUuid", "dojo/request/script"], + function (xhr, base64, digestsBase, MD5, uuid, script) { var encodeUTF8 = function encodeUTF8(str) { var byteArray = []; @@ -83,34 +83,32 @@ var saslPlain = function saslPlain(user, password, callbackFunction) var saslCramMD5 = function saslCramMD5(user, password, saslMechanism, callbackFunction) { - - // Using dojo.xhrGet, as very little information is being sent - dojo.xhrPost({ - // The URL of the request - url: "rest/sasl", - content: { - mechanism: saslMechanism - }, - handleAs: "json", - failOk: true - }).then(function(data) - { - - var challengeBytes = base64.decode(data.challenge); - var wa=[]; - var bitLength = challengeBytes.length*8; - for(var i=0; i>5] |= (challengeBytes[i/8] & 0xFF)<<(i%32); - } - var challengeStr = digestsBase.wordToString(wa).substring(0,challengeBytes.length); - var digest = user + " " + MD5._hmac(challengeStr, password, digestsBase.outputTypes.Hex); - var id = data.id; + var challengeBytes = base64.decode(data.challenge); + var wa=[]; + var bitLength = challengeBytes.length*8; + for(var i=0; i>5] |= (challengeBytes[i/8] & 0xFF)<<(i%32); + } + var challengeStr = digestsBase.wordToString(wa).substring(0,challengeBytes.length); + + var digest = user + " " + MD5._hmac(challengeStr, password, digestsBase.outputTypes.Hex); + var id = data.id; - var response = base64.encode(encodeUTF8( digest )); + var response = base64.encode(encodeUTF8( digest )); - dojo.xhrPost({ + dojo.xhrPost({ // The URL of the request url: "rest/sasl", content: { @@ -121,20 +119,163 @@ var saslCramMD5 = function saslCramMD5(user, password, saslMechanism, callbackFu failOk: true }).then(callbackFunction, errorHandler); - }, - function(error) - { - if(error.status == 403) + }, + function(error) { - alert("Authentication Failed"); - } - else - { - alert(error); - } - }); + if(error.status == 403) + { + alert("Authentication Failed"); + } + else + { + alert(error); + } + }); + + + }; + var saslScramSha1 = function saslScramSha1(user, password, saslMechanism, callbackFunction) + { + + script.get("webjars/cryptojs/3.1.2/rollups/hmac-sha1.js").then( function() + { + script.get("webjars/cryptojs/3.1.2/components/enc-base64-min.js").then ( function() + { + + var toBase64 = function toBase64( input ) + { + var result = []; + for(var i = 0; i < input.length; i++) + { + result[i] = input.charCodeAt(i); + } + return base64.encode( result ) + }; + + var fromBase64 = function fromBase64( input ) + { + var decoded = base64.decode( input ); + var result = ""; + for(var i = 0; i < decoded.length; i++) + { + result+= String.fromCharCode(decoded[i]); + } + return result; + }; + + var xor = function xor(lhs, rhs) { + var words = []; + for(var i = 0; i < lhs.words.length; i++) + { + words.push(lhs.words[i]^rhs.words[i]); + } + return CryptoJS.lib.WordArray.create(words); + }; + + var hasNonAscii = function hasNonAscii(name) { + for(var i = 0; i < name.length; i++) { + if(name.charCodeAt(i) > 127) { + return true; + } + } + return false; + }; + + var generateSaltedPassword = function generateSaltedPassword(salt, password, iterationCount) + { + var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, password); + + hmac.update(salt); + hmac.update(CryptoJS.enc.Hex.parse("00000001")); + + var result = hmac.finalize(); + var previous = null; + for(var i = 1 ;i < iterationCount; i++) + { + hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, password); + hmac.update( previous != null ? previous : result ); + previous = hmac.finalize(); + result = xor(result, previous); + } + return result; + + }; + + GS2_HEADER = "n,,"; + + if(!hasNonAscii(user)) { + + user = user.replace(/=/g, "=3D"); + user = user.replace(/,/g, "=2C"); + + clientNonce = uuid(); + clientFirstMessageBare = "n=" + user + ",r=" + clientNonce; + dojo.xhrPost({ + // The URL of the request + url: "rest/sasl", + content: { + mechanism: saslMechanism, + response: toBase64(GS2_HEADER + clientFirstMessageBare) + }, + handleAs: "json", + failOk: true + }).then(function (data) { + var serverFirstMessage = fromBase64(data.challenge); + var id = data.id; + + var parts = serverFirstMessage.split(","); + nonce = parts[0].substring(2); + if (!nonce.substr(0, clientNonce.length) == clientNonce) { + alert("Authentication error - server nonce does not start with client nonce") + } + else { + var salt = CryptoJS.enc.Base64.parse(parts[1].substring(2)); + var iterationCount = parts[2].substring(2); + var saltedPassword = generateSaltedPassword(salt, password, iterationCount) + var clientFinalMessageWithoutProof = "c=" + toBase64(GS2_HEADER) + ",r=" + nonce; + var authMessage = clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof; + var clientKey = CryptoJS.HmacSHA1("Client Key", saltedPassword); + var storedKey = CryptoJS.SHA1(clientKey); + var clientSignature = CryptoJS.HmacSHA1(authMessage, storedKey); + var clientProof = xor(clientKey, clientSignature); + var serverKey = CryptoJS.HmacSHA1("Server Key", saltedPassword); + serverSignature = CryptoJS.HmacSHA1(authMessage, serverKey); + dojo.xhrPost({ + // The URL of the request + url: "rest/sasl", + content: { + id: id, + response: toBase64(clientFinalMessageWithoutProof + + ",p=" + clientProof.toString(CryptoJS.enc.Base64)) + }, + handleAs: "json", + failOk: true + }).then(function (data) { + var serverFinalMessage = fromBase64(data.challenge); + if (serverSignature.toString(CryptoJS.enc.Base64) == serverFinalMessage.substring(2)) { + callbackFunction(); + } + else { + errorHandler("Server signature did not match"); + } + + + }, errorHandler); + } + + }, errorHandler); + } + else + { + alert("Username '"+name+"' is invalid"); + } + + }, errorHandler); + }, errorHandler); + }; + var containsMechanism = function containsMechanism(mechanisms, mech) { for (var i = 0; i < mechanisms.length; i++) { @@ -157,7 +298,11 @@ SaslClient.authenticate = function(username, password, callbackFunction) }).then(function(data) { var mechMap = data.mechanisms; - if (containsMechanism(mechMap, "CRAM-MD5")) + if(containsMechanism(mechMap, "SCRAM-SHA-1")) + { + saslScramSha1(username, password, "SCRAM-SHA-1", callbackFunction) + } + else if (containsMechanism(mechMap, "CRAM-MD5")) { saslCramMD5(username, password, "CRAM-MD5", callbackFunction); } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js index 3d349830ac..6d83a68fb4 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js @@ -140,7 +140,7 @@ define(["dojo/_base/xhr", util.isProviderManagingUsers = function(type) { - return (type === "PlainPasswordFile" || type === "Base64MD5PasswordFile"); + return (type === "PlainPasswordFile" || type === "Base64MD5PasswordFile" || type === "SCRAM-SHA1"); }; util.showSetAttributesDialog = function(attributeWidgetFactories, data, putURL, dialogTitle, appendNameToUrl) diff --git a/qpid/java/build.deps b/qpid/java/build.deps index 0aa35954bf..d799899171 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -59,6 +59,8 @@ jetty-servlet=lib/required/jetty-servlet-8.1.14.v20131031.jar jetty-websocket=lib/required/jetty-websocket-8.1.14.v20131031.jar servlet-api=${geronimo-servlet} +cryptojs=lib/required/cryptojs-3.1.2.jar + dojo-version=1.9.1 dojo=lib/required/dojo-${dojo-version}.zip @@ -83,7 +85,7 @@ broker-core.libs=${commons-cli} ${commons-logging} ${log4j} ${slf4j-log4j} \ #Borrow the broker-core libs, hack for release binary generation broker.libs=${broker-core.libs} -broker-plugins-management-http.libs=${dojo} +broker-plugins-management-http.libs=${dojo} ${cryptojs} broker-plugins.libs=${log4j} ${commons-configuration.libs} test.libs=${slf4j-log4j} ${log4j} ${junit} ${slf4j-api} ${mockito-all} 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 8855a040ea..109ff9942a 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 @@ -31,3 +31,5 @@ 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 +SCRAM-SHA1.8=org.apache.qpid.client.security.UsernamePasswordCallbackHandler + diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties index b903208927..c2cd671bdf 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties @@ -19,3 +19,4 @@ AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory ANONYMOUS=org.apache.qpid.client.security.anonymous.AnonymousSaslClientFactory +SCRAM-SHA1=org.apache.qpid.client.security.scram.ScramSHA1SaslClientFactory diff --git a/qpid/java/ivy.retrieve.xml b/qpid/java/ivy.retrieve.xml index 59b3fa70af..d4020b8534 100644 --- a/qpid/java/ivy.retrieve.xml +++ b/qpid/java/ivy.retrieve.xml @@ -72,6 +72,7 @@ + -- cgit v1.2.1 From cf179ab67843a62cb333663d8a9429610f3a47f3 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Sun, 23 Mar 2014 18:16:30 +0000 Subject: QPID-5633 : [Java Broker] Change the way StoreConfigurationChangeListener derives the "type" (actually category) of the ConfiguredObject git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1580559 13f79535-47bb-0310-9956-ffa450edef68 --- .../store/StoreConfigurationChangeListener.java | 94 +--------------------- .../apache/qpid/server/model/ConfiguredObject.java | 3 + .../model/adapter/AbstractConfiguredObject.java | 5 ++ .../StoreConfigurationChangeListenerTest.java | 5 ++ 4 files changed, 14 insertions(+), 93 deletions(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java index e030d50e56..addc42e6f9 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java @@ -87,7 +87,7 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList private ConfigurationEntry toConfigurationEntry(ConfiguredObject object) { - Class objectType = getConfiguredObjectType(object); + Class objectType = object.getCategoryClass(); Set childrenIds = getChildrenIds(object, objectType); ConfigurationEntry entry = new ConfigurationEntry(object.getId(), objectType.getSimpleName(), object.getActualAttributes(), childrenIds, _store); @@ -120,98 +120,6 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList return childrenIds; } - private Class getConfiguredObjectType(ConfiguredObject object) - { - if (object instanceof Broker) - { - return Broker.class; - } - else if (object instanceof VirtualHost) - { - return VirtualHost.class; - } - else if (object instanceof Port) - { - return Port.class; - } - else if (object instanceof AuthenticationProvider) - { - return AuthenticationProvider.class; - } - return getConfiguredObjectTypeFromImplementedInterfaces(object.getClass()); - } - - @SuppressWarnings("unchecked") - private Class getConfiguredObjectTypeFromImplementedInterfaces(Class objectClass) - { - // get all implemented interfaces extending ConfiguredObject - Set> interfaces = getImplementedInterfacesExtendingSuper(objectClass, ConfiguredObject.class); - - if (interfaces.size() == 0) - { - throw new StoreException("Can not identify the configured object type"); - } - - if (interfaces.size() == 1) - { - return (Class)interfaces.iterator().next(); - } - - Set> superInterfaces = new HashSet>(); - - // find all super interfaces - for (Class interfaceClass : interfaces) - { - for (Class interfaceClass2 : interfaces) - { - if (interfaceClass != interfaceClass2) - { - if (interfaceClass.isAssignableFrom(interfaceClass2)) - { - superInterfaces.add(interfaceClass); - } - } - } - } - - // remove super interfaces - for (Class superInterface : superInterfaces) - { - interfaces.remove(superInterface); - } - - if (interfaces.size() == 1) - { - return (Class)interfaces.iterator().next(); - } - else - { - throw new StoreException("Can not identify the configured object type as an it implements" - + " more than one configured object interfaces: " + interfaces); - } - - } - - private Set> getImplementedInterfacesExtendingSuper(Class classInstance, Class superInterface) - { - Set> interfaces = new HashSet>(); - Class[] classInterfaces = classInstance.getInterfaces(); - for (Class interfaceClass : classInterfaces) - { - if (interfaceClass!= superInterface && superInterface.isAssignableFrom(interfaceClass)) - { - interfaces.add(interfaceClass); - } - } - Class superClass = classInstance.getSuperclass(); - if (superClass != null) - { - Set> superClassInterfaces = getImplementedInterfacesExtendingSuper(superClass, superInterface); - interfaces.addAll(superClassInterfaces); - } - return interfaces; - } - @Override public String toString() { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java index b674f1e7db..740ccf8200 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -276,4 +276,7 @@ public interface ConfiguredObject> ConfiguredObject... otherParents); void setAttributes(Map attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException; + + Class getCategoryClass(); + } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java index 1bdb638765..2a12284f1f 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java @@ -256,6 +256,11 @@ public abstract class AbstractConfiguredObject> im return _name; } + public Class getCategoryClass() + { + return getCategory(getClass()); + } + public State getDesiredState() { return null; //TODO diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java index c23c4715e8..c5786fb981 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java @@ -63,7 +63,9 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); VirtualHost child = mock(VirtualHost.class); + when(child.getCategoryClass()).thenReturn(VirtualHost.class); _listener.childAdded(broker, child); verify(_store).save(any(ConfigurationEntry.class), any(ConfigurationEntry.class)); } @@ -72,7 +74,9 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); VirtualHost child = mock(VirtualHost.class); + when(child.getCategoryClass()).thenReturn(VirtualHost.class); _listener.childRemoved(broker, child); verify(_store).save(any(ConfigurationEntry.class)); } @@ -81,6 +85,7 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); _listener.attributeSet(broker, Broker.QUEUE_FLOW_CONTROL_SIZE_BYTES, null, 1); verify(_store).save(any(ConfigurationEntry.class)); } -- cgit v1.2.1 From a998e640d140cd3ef42e0126a88c248186601684 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Sun, 23 Mar 2014 18:23:24 +0000 Subject: QPID-5640 : [Java Broker] Allow the Json virtual host store to save bindings git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1580560 13f79535-47bb-0310-9956-ffa450edef68 --- .../qpid/server/store/JsonFileConfigStore.java | 41 +++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java index 3de601d741..e99c2019cd 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java @@ -38,10 +38,17 @@ import java.util.UUID; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Model; import org.apache.qpid.server.model.VirtualHost; +import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.Version; import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.JsonSerializer; +import org.codehaus.jackson.map.Module; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.module.SimpleModule; public class JsonFileConfigStore implements DurableConfigurationStore { @@ -61,8 +68,30 @@ public class JsonFileConfigStore implements DurableConfigurationStore private String _backupFileName; private int _configVersion; + private static final Module _module; + static + { + SimpleModule module= new SimpleModule("ConfiguredObjectSerializer", new Version(1,0,0,null)); + + final JsonSerializer serializer = new JsonSerializer() + { + @Override + public void serialize(final ConfiguredObject value, + final JsonGenerator jgen, + final SerializerProvider provider) + throws IOException, JsonProcessingException + { + jgen.writeString(value.getId().toString()); + } + }; + module.addSerializer(ConfiguredObject.class, serializer); + + _module = module; + } + public JsonFileConfigStore() { + _objectMapper.registerModule(_module); _objectMapper.enable(SerializationConfig.Feature.INDENT_OUTPUT); } @@ -414,7 +443,17 @@ public class JsonFileConfigStore implements DurableConfigurationStore { ConfiguredObjectRecord childRecord = _objectsById.get(childId); final String parentArg = type.getSimpleName().toLowerCase(); - if(id.toString().equals(String.valueOf(childRecord.getAttributes().get(parentArg)))) + final Object parent = childRecord.getAttributes().get(parentArg); + String parentId; + if(parent instanceof ConfiguredObject) + { + parentId = ((ConfiguredObject)parent).getId().toString(); + } + else + { + parentId = String.valueOf(parent); + } + if(id.toString().equals(parentId)) { entities.add(build(childClass,childId)); } -- cgit v1.2.1 From d6218a66346b3b4005d61cdcb26c4b7b0eb71cdd Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Sun, 23 Mar 2014 18:29:22 +0000 Subject: QPID-5615 : [Java Broker] update the durable configuration store interface to be entirely in terms of ConfiguredObjectRecords and give stores knowledge of hierarchy git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1580561 13f79535-47bb-0310-9956-ffa450edef68 --- .../store/berkeleydb/AbstractBDBMessageStore.java | 245 ++++---- .../berkeleydb/BDBConfiguredObjectRecord.java | 107 ++++ .../server/store/berkeleydb/BDBHAMessageStore.java | 12 +- .../server/store/berkeleydb/BDBMessageStore.java | 6 +- .../store/berkeleydb/entry/HierarchyKey.java | 79 +++ .../berkeleydb/tuple/ConfiguredObjectBinding.java | 5 +- .../berkeleydb/tuple/HierarchyKeyBinding.java | 59 ++ .../store/berkeleydb/upgrade/StoreUpgrade.java | 3 +- .../store/berkeleydb/upgrade/UpgradeFrom4To5.java | 3 +- .../store/berkeleydb/upgrade/UpgradeFrom5To6.java | 5 +- .../store/berkeleydb/upgrade/UpgradeFrom6To7.java | 3 +- .../store/berkeleydb/upgrade/UpgradeFrom7To8.java | 159 ++++++ .../server/store/berkeleydb/upgrade/Upgrader.java | 9 +- .../store/berkeleydb/HAMessageStoreSmokeTest.java | 4 +- .../tuple/ConfiguredObjectBindingTest.java | 3 +- .../upgrade/AbstractUpgradeTestCase.java | 11 +- .../berkeleydb/upgrade/UpgradeFrom4to5Test.java | 4 +- .../berkeleydb/upgrade/UpgradeFrom5To6Test.java | 22 +- .../upgrade/UpgraderFailOnNewerVersionTest.java | 2 +- .../store/berkeleydb/upgrade/UpgraderTest.java | 4 +- .../store/berkeleydb/BDBMessageStoreTest.java | 14 +- .../apache/qpid/server/model/ConfiguredObject.java | 4 + .../org/apache/qpid/server/model/VirtualHost.java | 3 + .../model/adapter/AbstractConfiguredObject.java | 57 ++ .../server/model/adapter/VirtualHostAdapter.java | 7 + .../AbstractDurableConfiguredObjectRecoverer.java | 15 +- .../server/store/AbstractJDBCMessageStore.java | 615 ++++++++++++--------- .../server/store/ConfigurationRecoveryHandler.java | 2 +- .../qpid/server/store/ConfiguredObjectRecord.java | 63 +-- .../server/store/ConfiguredObjectRecordImpl.java | 104 ++++ .../store/DurableConfigurationRecoverer.java | 34 +- .../server/store/DurableConfigurationStore.java | 30 +- .../store/DurableConfigurationStoreHelper.java | 38 +- .../store/DurableConfigurationStoreUpgrader.java | 3 +- .../store/DurableConfiguredObjectRecoverer.java | 3 +- .../qpid/server/store/JsonFileConfigStore.java | 147 +++-- .../apache/qpid/server/store/NonNullUpgrader.java | 12 + .../apache/qpid/server/store/NullMessageStore.java | 23 +- .../org/apache/qpid/server/store/NullUpgrader.java | 10 +- .../qpid/server/virtualhost/BindingRecoverer.java | 21 +- .../virtualhost/DefaultUpgraderProvider.java | 80 +-- .../qpid/server/virtualhost/ExchangeRecoverer.java | 7 +- .../qpid/server/virtualhost/QueueRecoverer.java | 8 +- .../AbstractDurableConfigurationStoreTestCase.java | 120 +++- .../qpid/server/store/JsonFileConfigStoreTest.java | 113 ++-- .../DurableConfigurationRecovererTest.java | 93 ++-- .../apache/qpid/server/store/MessageStoreTest.java | 6 + .../apache/qpid/server/store/SlowMessageStore.java | 24 +- 48 files changed, 1606 insertions(+), 795 deletions(-) create mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java create mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java create mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java create mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java (limited to 'qpid/java') diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java index 7d7ebd32fe..9a06f06999 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java @@ -41,8 +41,6 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.message.EnqueueableMessage; @@ -51,16 +49,11 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.*; import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler; import org.apache.qpid.server.store.TransactionLogRecoveryHandler.QueueEntryRecoveryHandler; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; import org.apache.qpid.server.store.berkeleydb.entry.PreparedTransaction; import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey; import org.apache.qpid.server.store.berkeleydb.entry.Xid; -import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.ContentBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.MessageMetaDataBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.PreparedTransactionBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.QueueEntryBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.UUIDTupleBinding; -import org.apache.qpid.server.store.berkeleydb.tuple.XidBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.*; import org.apache.qpid.server.store.berkeleydb.upgrade.Upgrader; import org.apache.qpid.util.FileUtils; @@ -70,7 +63,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private static final int LOCK_RETRY_ATTEMPTS = 5; - public static final int VERSION = 7; + public static final int VERSION = 8; private static final Map ENVCONFIG_DEFAULTS = Collections.unmodifiableMap(new HashMap() {{ @@ -83,6 +76,8 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private Environment _environment; private static String CONFIGURED_OBJECTS = "CONFIGURED_OBJECTS"; + private static String CONFIGURED_OBJECT_HIERARCHY = "CONFIGURED_OBJECT_HIERARCHY"; + private static String MESSAGEMETADATADB_NAME = "MESSAGE_METADATA"; private static String MESSAGECONTENTDB_NAME = "MESSAGE_CONTENT"; private static String DELIVERYDB_NAME = "QUEUE_ENTRIES"; @@ -92,6 +87,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private static String CONFIG_VERSION_DB = "CONFIG_VERSION"; private Database _configuredObjectsDb; + private Database _configuredObjectHierarchyDb; private Database _configVersionDb; private Database _messageMetaDataDb; private Database _messageContentDb; @@ -146,6 +142,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private Map _envConfigMap; private VirtualHost _virtualHost; + public AbstractBDBMessageStore() { _stateManager = new StateManager(_eventManager); @@ -183,7 +180,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private void completeInitialisation() { - configure(_virtualHost); + configure(); _stateManager.attainState(State.INITIALISED); } @@ -221,27 +218,24 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo /** * Called after instantiation in order to configure the message store. * - * - * - * @param virtualHost The virtual host using this store * @return whether a new store environment was created or not (to indicate whether recovery is necessary) * * @throws Exception If any error occurs that means the store is unable to configure itself. */ - public void configure(VirtualHost virtualHost) + public void configure() { - configure(virtualHost, _messageRecoveryHandler != null); + configure(_messageRecoveryHandler != null); } - public void configure(VirtualHost virtualHost, boolean isMessageStore) + public void configure(boolean isMessageStore) { - String name = virtualHost.getName(); + String name = _virtualHost.getName(); final String defaultPath = System.getProperty("QPID_WORK") + File.separator + "bdbstore" + File.separator + name; String storeLocation; if(isMessageStore) { - storeLocation = (String) virtualHost.getAttribute(VirtualHost.STORE_PATH); + storeLocation = (String) _virtualHost.getAttribute(VirtualHost.STORE_PATH); if(storeLocation == null) { storeLocation = defaultPath; @@ -249,15 +243,15 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo } else // we are acting only as the durable config store { - storeLocation = (String) virtualHost.getAttribute(VirtualHost.CONFIG_STORE_PATH); + storeLocation = (String) _virtualHost.getAttribute(VirtualHost.CONFIG_STORE_PATH); if(storeLocation == null) { storeLocation = defaultPath; } } - Object overfullAttr = virtualHost.getAttribute(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE); - Object underfullAttr = virtualHost.getAttribute(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE); + Object overfullAttr = _virtualHost.getAttribute(MessageStoreConstants.OVERFULL_SIZE_ATTRIBUTE); + Object underfullAttr = _virtualHost.getAttribute(MessageStoreConstants.UNDERFULL_SIZE_ATTRIBUTE); _persistentSizeHighThreshold = overfullAttr == null ? -1l : overfullAttr instanceof Number ? ((Number) overfullAttr).longValue() : Long.parseLong(overfullAttr.toString()); @@ -285,7 +279,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo _envConfigMap = new HashMap(); _envConfigMap.putAll(ENVCONFIG_DEFAULTS); - Object bdbEnvConfigAttr = virtualHost.getAttribute("bdbEnvironmentConfig"); + Object bdbEnvConfigAttr = _virtualHost.getAttribute("bdbEnvironmentConfig"); if(bdbEnvConfigAttr instanceof Map) { _envConfigMap.putAll((Map)bdbEnvConfigAttr); @@ -293,27 +287,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo LOGGER.info("Configuring BDB message store"); - setupStore(environmentPath, name); - } - - protected Map getConfigMap(Map defaultConfig, Configuration config, String prefix) throws ConfigurationException - { - final List argumentNames = config.getList(prefix + ".name"); - final List argumentValues = config.getList(prefix + ".value"); - final int initialSize = argumentNames.size() + defaultConfig.size(); - - final Map attributes = new HashMap(initialSize); - attributes.putAll(defaultConfig); - - for (int i = 0; i < argumentNames.size(); i++) - { - final String argName = argumentNames.get(i).toString(); - final String argValue = argumentValues.get(i).toString(); - - attributes.put(argName, argValue); - } - - return Collections.unmodifiableMap(attributes); + setupStore(environmentPath); } @Override @@ -337,11 +311,11 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo _stateManager.attainState(State.ACTIVE); } - protected void setupStore(File storePath, String name) + protected void setupStore(File storePath) { _environment = createEnvironment(storePath); - new Upgrader(_environment, name).upgradeIfNecessary(); + new Upgrader(_environment, _virtualHost).upgradeIfNecessary(); openDatabases(); @@ -355,6 +329,11 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo return _environment; } + protected final VirtualHost getVirtualHost() + { + return _virtualHost; + } + private void openDatabases() throws DatabaseException { DatabaseConfig dbConfig = new DatabaseConfig(); @@ -365,6 +344,7 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo dbConfig.setReadOnly(false); _configuredObjectsDb = openDatabase(CONFIGURED_OBJECTS, dbConfig); + _configuredObjectHierarchyDb = openDatabase(CONFIGURED_OBJECT_HIERARCHY, dbConfig); _configVersionDb = openDatabase(CONFIG_VERSION_DB, dbConfig); _messageMetaDataDb = openDatabase(MESSAGEMETADATADB_NAME, dbConfig); _messageContentDb = openDatabase(MESSAGECONTENTDB_NAME, dbConfig); @@ -411,11 +391,17 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo _messageContentDb.close(); } - if (_configuredObjectsDb != null) - { - LOGGER.info("Closing configurable objects database"); - _configuredObjectsDb.close(); - } + if (_configuredObjectsDb != null) + { + LOGGER.info("Closing configurable objects database"); + _configuredObjectsDb.close(); + } + + if (_configuredObjectHierarchyDb != null) + { + LOGGER.info("Closing configurable object hierarchy database"); + _configuredObjectHierarchyDb.close(); + } if (_deliveryDb != null) { @@ -555,24 +541,60 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo private void loadConfiguredObjects(ConfigurationRecoveryHandler crh) throws DatabaseException { - Cursor cursor = null; + Cursor objectsCursor = null; + Cursor hierarchyCursor = null; try { - cursor = _configuredObjectsDb.openCursor(null, null); + objectsCursor = _configuredObjectsDb.openCursor(null, null); DatabaseEntry key = new DatabaseEntry(); DatabaseEntry value = new DatabaseEntry(); - while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + + Map configuredObjects = + new HashMap(); + + while (objectsCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) { UUID id = UUIDTupleBinding.getInstance().entryToObject(key); - ConfiguredObjectRecord configuredObject = new ConfiguredObjectBinding(id).entryToObject(value); - crh.configuredObject(configuredObject.getId(),configuredObject.getType(),configuredObject.getAttributes()); + BDBConfiguredObjectRecord configuredObject = + (BDBConfiguredObjectRecord) new ConfiguredObjectBinding(id).entryToObject(value); + configuredObjects.put(configuredObject.getId(), configuredObject); } + // set parents + hierarchyCursor = _configuredObjectHierarchyDb.openCursor(null, null); + while (hierarchyCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + { + HierarchyKey hk = HierarchyKeyBinding.getInstance().entryToObject(key); + UUID parentId = UUIDTupleBinding.getInstance().entryToObject(value); + BDBConfiguredObjectRecord child = configuredObjects.get(hk.getChildId()); + if(child != null) + { + ConfiguredObjectRecord parent = configuredObjects.get(parentId); + if(parent != null) + { + child.addParent(hk.getParentType(), parent); + } + else if(hk.getParentType().equals("Exchange")) + { + // TODO - remove this hack for the pre-defined exchanges + child.addParent(hk.getParentType(), new BDBConfiguredObjectRecord(parentId, "Exchange", Collections.emptyMap())); + } + } + } + + for (ConfiguredObjectRecord record : configuredObjects.values()) + { + crh.configuredObject(record); + } + + + } finally { - closeCursorSafely(cursor); + closeCursorSafely(objectsCursor); + closeCursorSafely(hierarchyCursor); } } @@ -842,39 +864,28 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo } @Override - public void create(UUID id, String type, Map attributes) throws StoreException + public void create(ConfiguredObjectRecord configuredObject) throws StoreException { + if (_stateManager.isInState(State.ACTIVE)) { - ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(id, type, attributes); - storeConfiguredObjectEntry(configuredObject); - } - } + com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null); - @Override - public void remove(UUID id, String type) throws StoreException - { - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("public void remove(id = " + id + ", type="+type+"): called"); - } - OperationStatus status = removeConfiguredObject(null, id); - if (status == OperationStatus.NOTFOUND) - { - throw new StoreException("Configured object of type " + type + " with id " + id + " not found"); + storeConfiguredObjectEntry(txn, configuredObject); + + txn.commit(); } } - @Override - public UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException + public UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null); Collection removed = new ArrayList(objects.length); - for(UUID id : objects) + for(ConfiguredObjectRecord record : objects) { - if(removeConfiguredObject(txn, id) == OperationStatus.SUCCESS) + if(removeConfiguredObject(txn, record) == OperationStatus.SUCCESS) { - removed.add(id); + removed.add(record.getId()); } } @@ -882,52 +893,50 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo return removed.toArray(new UUID[removed.size()]); } - @Override - public void update(UUID id, String type, Map attributes) throws StoreException - { - update(false, id, type, attributes, null); - } - public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException { com.sleepycat.je.Transaction txn = _environment.beginTransaction(null, null); for(ConfiguredObjectRecord record : records) { - update(createIfNecessary, record.getId(), record.getType(), record.getAttributes(), txn); + update(createIfNecessary, record, txn); } txn.commit(); } - private void update(boolean createIfNecessary, UUID id, String type, Map attributes, com.sleepycat.je.Transaction txn) throws + private void update(boolean createIfNecessary, ConfiguredObjectRecord record, com.sleepycat.je.Transaction txn) throws StoreException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Updating " +type + ", id: " + id); + LOGGER.debug("Updating " + record.getType() + ", id: " + record.getId()); } try { DatabaseEntry key = new DatabaseEntry(); UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance(); - keyBinding.objectToEntry(id, key); + keyBinding.objectToEntry(record.getId(), key); DatabaseEntry value = new DatabaseEntry(); DatabaseEntry newValue = new DatabaseEntry(); ConfiguredObjectBinding configuredObjectBinding = ConfiguredObjectBinding.getInstance(); OperationStatus status = _configuredObjectsDb.get(txn, key, value, LockMode.DEFAULT); - if (status == OperationStatus.SUCCESS || (createIfNecessary && status == OperationStatus.NOTFOUND)) + final boolean isNewRecord = status == OperationStatus.NOTFOUND; + if (status == OperationStatus.SUCCESS || (createIfNecessary && isNewRecord)) { - ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes); // write the updated entry to the store - configuredObjectBinding.objectToEntry(newQueueRecord, newValue); + configuredObjectBinding.objectToEntry(record, newValue); status = _configuredObjectsDb.put(txn, key, newValue); if (status != OperationStatus.SUCCESS) { throw new StoreException("Error updating queue details within the store: " + status); } + if(isNewRecord) + { + writeHierarchyRecords(txn, record); + } } else if (status != OperationStatus.NOTFOUND) { @@ -1441,17 +1450,19 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo /** * Makes the specified configured object persistent. * + * + * @param txn * @param configuredObject Details of the configured object to store. * @throws org.apache.qpid.server.store.StoreException If the operation fails for any reason. */ - private void storeConfiguredObjectEntry(ConfiguredObjectRecord configuredObject) throws StoreException + private void storeConfiguredObjectEntry(final Transaction txn, ConfiguredObjectRecord configuredObject) throws StoreException { if (_stateManager.isInState(State.ACTIVE)) { LOGGER.debug("Storing configured object: " + configuredObject); DatabaseEntry key = new DatabaseEntry(); - UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance(); - keyBinding.objectToEntry(configuredObject.getId(), key); + UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance(); + uuidBinding.objectToEntry(configuredObject.getId(), key); DatabaseEntry value = new DatabaseEntry(); ConfiguredObjectBinding queueBinding = ConfiguredObjectBinding.getInstance(); @@ -1459,12 +1470,13 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo queueBinding.objectToEntry(configuredObject, value); try { - OperationStatus status = _configuredObjectsDb.put(null, key, value); + OperationStatus status = _configuredObjectsDb.put(txn, key, value); if (status != OperationStatus.SUCCESS) { throw new StoreException("Error writing configured object " + configuredObject + " to database: " + status); } + writeHierarchyRecords(txn, configuredObject); } catch (DatabaseException e) { @@ -1474,16 +1486,49 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo } } - private OperationStatus removeConfiguredObject(Transaction tx, UUID id) throws StoreException + private void writeHierarchyRecords(final Transaction txn, final ConfiguredObjectRecord configuredObject) { + OperationStatus status; + HierarchyKeyBinding hierarchyBinding = HierarchyKeyBinding.getInstance(); + DatabaseEntry hierarchyKey = new DatabaseEntry(); + DatabaseEntry hierarchyValue = new DatabaseEntry(); + + for(Map.Entry parent : configuredObject.getParents().entrySet()) + { + + hierarchyBinding.objectToEntry(new HierarchyKey(configuredObject.getId(), parent.getKey()), hierarchyKey); + UUIDTupleBinding.getInstance().objectToEntry(parent.getValue().getId(), hierarchyValue); + status = _configuredObjectHierarchyDb.put(txn, hierarchyKey, hierarchyValue); + if (status != OperationStatus.SUCCESS) + { + throw new StoreException("Error writing configured object " + configuredObject + " parent record to database: " + + status); + } + } + } + private OperationStatus removeConfiguredObject(Transaction tx, ConfiguredObjectRecord record) throws StoreException + { + UUID id = record.getId(); + Map parents = record.getParents(); LOGGER.debug("Removing configured object: " + id); DatabaseEntry key = new DatabaseEntry(); UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance(); uuidBinding.objectToEntry(id, key); try { - return _configuredObjectsDb.delete(tx, key); + OperationStatus status = _configuredObjectsDb.delete(tx, key); + if(status == OperationStatus.SUCCESS) + { + for(String parentType : parents.keySet()) + { + DatabaseEntry hierarchyKey = new DatabaseEntry(); + HierarchyKeyBinding keyBinding = HierarchyKeyBinding.getInstance(); + keyBinding.objectToEntry(new HierarchyKey(record.getId(), parentType), hierarchyKey); + _configuredObjectHierarchyDb.delete(tx, hierarchyKey); + } + } + return status; } catch (DatabaseException e) { @@ -1859,4 +1904,10 @@ public abstract class AbstractBDBMessageStore implements MessageStore, DurableCo } } } + + void setVirtualHost(final VirtualHost virtualHost) + { + _virtualHost = virtualHost; + } + } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java new file mode 100644 index 0000000000..f13e4dd08b --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java @@ -0,0 +1,107 @@ +/* + * + * 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.berkeleydb; + +import org.apache.qpid.server.store.ConfiguredObjectRecord; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class BDBConfiguredObjectRecord implements ConfiguredObjectRecord +{ + private final UUID _id; + private final String _type; + private final Map _attributes; + private Map _parents = new HashMap(); + + public BDBConfiguredObjectRecord(final UUID id, final String type, final Map attributes) + { + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(attributes); + } + + public UUID getId() + { + return _id; + } + + public String getType() + { + return _type; + } + + public Map getAttributes() + { + return _attributes; + } + + void addParent(String parentType, ConfiguredObjectRecord parent) + { + _parents.put(parentType, parent); + } + + @Override + public Map getParents() + { + return Collections.unmodifiableMap(_parents); + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final BDBConfiguredObjectRecord that = (BDBConfiguredObjectRecord) o; + + if (_attributes != null ? !_attributes.equals(that._attributes) : that._attributes != null) + { + return false; + } + if (_id != null ? !_id.equals(that._id) : that._id != null) + { + return false; + } + if (_type != null ? !_type.equals(that._type) : that._type != null) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _id != null ? _id.hashCode() : 0; + result = 31 * result + (_type != null ? _type.hashCode() : 0); + return result; + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java index d99733acf0..bdd48e99bb 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java @@ -37,7 +37,6 @@ import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.TaskPrincipal; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.store.HAMessageStore; @@ -124,9 +123,10 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess private Map _repConfig; @Override - public void configure(VirtualHost virtualHost) + public void configure() { //Mandatory configuration + VirtualHost virtualHost = getVirtualHost(); _groupName = getValidatedStringAttribute(virtualHost, "haGroupName"); _nodeName = getValidatedStringAttribute(virtualHost, "haNodeName"); _nodeHostPort = getValidatedStringAttribute(virtualHost, "haNodeAddress"); @@ -159,7 +159,7 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess + "! Please set highAvailability.coalescingSync to false in store configuration."); } - super.configure(virtualHost); + super.configure(); } @@ -207,13 +207,13 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess @Override - protected void setupStore(File storePath, String name) throws DatabaseException + protected void setupStore(File storePath) throws DatabaseException { - super.setupStore(storePath, name); + super.setupStore(storePath); if(_coalescingSync) { - _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + name, getEnvironment()); + _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + getVirtualHost().getName(), getEnvironment()); _commitThreadWrapper.startCommitThread(); } } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java index acff8e2b21..0fd5060397 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java @@ -46,11 +46,11 @@ public class BDBMessageStore extends AbstractBDBMessageStore private CommitThreadWrapper _commitThreadWrapper; @Override - protected void setupStore(File storePath, String name) throws DatabaseException + protected void setupStore(File storePath) throws DatabaseException { - super.setupStore(storePath, name); + super.setupStore(storePath); - _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + name, getEnvironment()); + _commitThreadWrapper = new CommitThreadWrapper("Commit-Thread-" + getVirtualHost().getName(), getEnvironment()); _commitThreadWrapper.startCommitThread(); } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java new file mode 100644 index 0000000000..d1c341447e --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.entry; + +import java.util.UUID; + +public class HierarchyKey +{ + private final UUID _childId; + private final String _parentType; + + public HierarchyKey(final UUID childId, final String parentType) + { + _childId = childId; + _parentType = parentType; + } + + public UUID getChildId() + { + return _childId; + } + + public String getParentType() + { + return _parentType; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final HierarchyKey that = (HierarchyKey) o; + + if (!_childId.equals(that._childId)) + { + return false; + } + if (!_parentType.equals(that._parentType)) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _childId.hashCode(); + result = 31 * result + _parentType.hashCode(); + return result; + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java index bc3beeb78b..38a2215fe7 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java @@ -32,6 +32,7 @@ import com.sleepycat.bind.tuple.TupleBinding; import com.sleepycat.bind.tuple.TupleInput; import com.sleepycat.bind.tuple.TupleOutput; import org.apache.qpid.server.store.StoreException; +import org.apache.qpid.server.store.berkeleydb.BDBConfiguredObjectRecord; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonProcessingException; @@ -80,7 +81,7 @@ public class ConfiguredObjectBinding extends TupleBinding value = mapper.readValue(json, Map.class); - ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(_uuid, type, value); + BDBConfiguredObjectRecord configuredObject = new BDBConfiguredObjectRecord(_uuid, type, value); return configuredObject; } catch (IOException e) diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java new file mode 100644 index 0000000000..13adaabfc8 --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java @@ -0,0 +1,59 @@ +/* + * + * 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.berkeleydb.tuple; + +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; +import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey; + +import java.util.UUID; + +public class HierarchyKeyBinding extends TupleBinding +{ + + private static final HierarchyKeyBinding INSTANCE = new HierarchyKeyBinding(); + + public static HierarchyKeyBinding getInstance() + { + return INSTANCE; + } + + /** private constructor forces getInstance instead */ + private HierarchyKeyBinding() { } + + public HierarchyKey entryToObject(TupleInput tupleInput) + { + UUID childId = new UUID(tupleInput.readLong(), tupleInput.readLong()); + String parentType = tupleInput.readString(); + + return new HierarchyKey(childId, parentType); + } + + public void objectToEntry(HierarchyKey hk, TupleOutput tupleOutput) + { + UUID uuid = hk.getChildId(); + tupleOutput.writeLong(uuid.getMostSignificantBits()); + tupleOutput.writeLong(uuid.getLeastSignificantBits()); + tupleOutput.writeString(hk.getParentType()); + } +} \ No newline at end of file diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java index adcaef35ef..b06b6d533b 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java @@ -21,8 +21,9 @@ package org.apache.qpid.server.store.berkeleydb.upgrade; import com.sleepycat.je.Environment; +import org.apache.qpid.server.model.VirtualHost; public interface StoreUpgrade { - void performUpgrade(Environment environment, UpgradeInteractionHandler handler, String virtualHostName); + void performUpgrade(Environment environment, UpgradeInteractionHandler handler, VirtualHost virtualHost); } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java index 87f8afde4a..3eac47c81b 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java @@ -39,6 +39,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.protocol.v0_8.MessageMetaData; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.store.StorableMessageMetaData; @@ -74,7 +75,7 @@ public class UpgradeFrom4To5 extends AbstractStoreUpgrade private static final Logger _logger = Logger.getLogger(UpgradeFrom4To5.class); - public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, VirtualHost virtualHost) { Transaction transaction = null; reportStarting(environment, 4); diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java index 46f2afd741..dea8421a33 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java @@ -44,6 +44,7 @@ import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.queue.QueueArgumentsConverter; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding; @@ -118,11 +119,11 @@ public class UpgradeFrom5To6 extends AbstractStoreUpgrade * Queue, Exchange, Bindings entries are stored now as configurable objects * in "CONFIGURED_OBJECTS" table. */ - public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, VirtualHost virtualHost) { reportStarting(environment, 5); upgradeMessages(environment, handler); - upgradeConfiguredObjectsAndDependencies(environment, handler, virtualHostName); + upgradeConfiguredObjectsAndDependencies(environment, handler, virtualHost.getName()); renameDatabases(environment, null); reportFinished(environment, 6); } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java index ce00fd1a48..79314ae098 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java @@ -27,6 +27,7 @@ import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.Environment; import com.sleepycat.je.OperationStatus; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.store.StoreException; public class UpgradeFrom6To7 extends AbstractStoreUpgrade @@ -35,7 +36,7 @@ public class UpgradeFrom6To7 extends AbstractStoreUpgrade private static final int DEFAULT_CONFIG_VERSION = 0; @Override - public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, VirtualHost virtualHost) { reportStarting(environment, 6); DatabaseConfig dbConfig = new DatabaseConfig(); diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java new file mode 100644 index 0000000000..3756c11d0c --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.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.server.store.berkeleydb.upgrade; + +import com.sleepycat.bind.tuple.ByteBinding; +import com.sleepycat.bind.tuple.IntegerBinding; +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import com.sleepycat.je.*; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.StoreException; +import org.apache.qpid.server.store.berkeleydb.BDBConfiguredObjectRecord; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; +import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.HierarchyKeyBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.UUIDTupleBinding; +import org.codehaus.jackson.map.ObjectMapper; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class UpgradeFrom7To8 extends AbstractStoreUpgrade +{ + + @Override + public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, VirtualHost virtualHost) + { + reportStarting(environment, 7); + + DatabaseConfig dbConfig = new DatabaseConfig(); + dbConfig.setTransactional(true); + dbConfig.setAllowCreate(true); + + Database hierarchyDb = environment.openDatabase(null, "CONFIGURED_OBJECT_HIERARCHY", dbConfig); + Database configuredObjectsDb = environment.openDatabase(null, "CONFIGURED_OBJECTS", dbConfig); + + Cursor objectsCursor = null; + + Transaction txn = environment.beginTransaction(null, null); + + try + { + objectsCursor = configuredObjectsDb.openCursor(txn, null); + DatabaseEntry key = new DatabaseEntry(); + DatabaseEntry value = new DatabaseEntry(); + + Map configuredObjects = + new HashMap(); + + while (objectsCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + { + UUID id = UUIDTupleBinding.getInstance().entryToObject(key); + TupleInput input = TupleBinding.entryToInput(value); + String type = input.readString(); + + if(!type.endsWith("Binding")) + { + UUIDTupleBinding.getInstance().objectToEntry(virtualHost.getId(),value); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("VirtualHost"); + TupleBinding.outputToEntry(tupleOutput, key); + hierarchyDb.put(txn, key, value); + } + else + { + String json = input.readString(); + ObjectMapper mapper = new ObjectMapper(); + try + { + DatabaseEntry hierarchyKey = new DatabaseEntry(); + DatabaseEntry hierarchyValue = new DatabaseEntry(); + + Map attributes = mapper.readValue(json, Map.class); + Object queueIdString = attributes.remove("queue"); + if(queueIdString instanceof String) + { + UUID queueId = UUID.fromString(queueIdString.toString()); + UUIDTupleBinding.getInstance().objectToEntry(queueId,hierarchyValue); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("Queue"); + TupleBinding.outputToEntry(tupleOutput, hierarchyKey); + hierarchyDb.put(txn, hierarchyKey, hierarchyValue); + } + Object exchangeIdString = attributes.remove("exchange"); + if(exchangeIdString instanceof String) + { + UUID exchangeId = UUID.fromString(exchangeIdString.toString()); + UUIDTupleBinding.getInstance().objectToEntry(exchangeId,hierarchyValue); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("Exchange"); + TupleBinding.outputToEntry(tupleOutput, hierarchyKey); + hierarchyDb.put(txn, hierarchyKey, hierarchyValue); + } + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeString(type); + StringWriter writer = new StringWriter(); + mapper.writeValue(writer,attributes); + tupleOutput.writeString(writer.getBuffer().toString()); + TupleBinding.outputToEntry(tupleOutput, value); + objectsCursor.putCurrent(value); + } + catch (IOException e) + { + throw new StoreException(e); + } + + } + + + } + + + } + finally + { + if(objectsCursor != null) + { + objectsCursor.close(); + } + } + txn.commit(); + + hierarchyDb.close(); + configuredObjectsDb.close(); + + + + reportFinished(environment, 8); + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java index 4d536a2f95..e960518719 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java @@ -24,6 +24,7 @@ import com.sleepycat.je.Cursor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.store.berkeleydb.AbstractBDBMessageStore; @@ -41,12 +42,12 @@ public class Upgrader static final String VERSION_DB_NAME = "DB_VERSION"; private Environment _environment; - private String _virtualHostName; + private VirtualHost _virtualHost; - public Upgrader(Environment environment, String virtualHostName) + public Upgrader(Environment environment, VirtualHost virtualHost) { _environment = environment; - _virtualHostName = virtualHostName; + _virtualHost = virtualHost; } public void upgradeIfNecessary() @@ -146,7 +147,7 @@ public class Upgrader + "UpgradeFrom"+fromVersion+"To"+toVersion); Constructor ctr = upgradeClass.getConstructor(); StoreUpgrade upgrade = ctr.newInstance(); - upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, _virtualHostName); + upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, _virtualHost); } catch (ClassNotFoundException e) { diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java index 7f7b65f315..6a42b2691f 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAMessageStoreSmokeTest.java @@ -19,7 +19,6 @@ */ package org.apache.qpid.server.store.berkeleydb; -import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.util.ServerScopedRuntimeException; import org.apache.qpid.test.utils.QpidTestCase; @@ -34,7 +33,8 @@ public class HAMessageStoreSmokeTest extends QpidTestCase { try { - _store.configure(mock(VirtualHost.class)); + _store.setVirtualHost(mock(VirtualHost.class)); + _store.configure(); fail("Expected an exception to be thrown"); } catch (ServerScopedRuntimeException ce) diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java index 5a5d39081c..965cad1cb5 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java @@ -29,6 +29,7 @@ import org.apache.qpid.server.store.ConfiguredObjectRecord; import com.sleepycat.bind.tuple.TupleInput; import com.sleepycat.bind.tuple.TupleOutput; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; public class ConfiguredObjectBindingTest extends TestCase { @@ -46,7 +47,7 @@ public class ConfiguredObjectBindingTest extends TestCase { super.setUp(); _configuredObjectBinding = ConfiguredObjectBinding.getInstance(); - _object = new ConfiguredObjectRecord(UUIDGenerator.generateRandomUUID(), DUMMY_TYPE_STRING, + _object = new ConfiguredObjectRecordImpl(UUIDGenerator.generateRandomUUID(), DUMMY_TYPE_STRING, DUMMY_ATTRIBUTES_MAP); } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java index b2b28b3c2d..ce143aba1b 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java @@ -25,12 +25,16 @@ import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPrepare import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.PRIORITY_QUEUE_NAME; import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.QUEUE_NAME; import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.QUEUE_WITH_DLQ_NAME; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.File; import java.io.InputStream; +import java.util.UUID; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.subjects.TestBlankSubject; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.util.FileUtils; @@ -167,8 +171,11 @@ public abstract class AbstractUpgradeTestCase extends QpidTestCase return count.longValue(); } - public String getVirtualHostName() + public VirtualHost getVirtualHost() { - return getName(); + VirtualHost virtualHost = mock(VirtualHost.class); + when(virtualHost.getName()).thenReturn(getName()); + when(virtualHost.getId()).thenReturn(UUID.randomUUID()); + return virtualHost; } } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java index 500fb0a919..d0f9455d9a 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java @@ -73,7 +73,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase public void testPerformUpgradeWithHandlerAnsweringYes() throws Exception { UpgradeFrom4To5 upgrade = new UpgradeFrom4To5(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHost()); assertQueues(new HashSet(Arrays.asList(QUEUE_NAMES))); @@ -103,7 +103,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase public void testPerformUpgradeWithHandlerAnsweringNo() throws Exception { UpgradeFrom4To5 upgrade = new UpgradeFrom4To5(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.NO), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.NO), getVirtualHost()); HashSet queues = new HashSet(Arrays.asList(QUEUE_NAMES)); assertTrue(NON_DURABLE_QUEUE_NAME + " should be in the list of queues" , queues.remove(NON_DURABLE_QUEUE_NAME)); diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java index 701fd94115..0460b1ce4c 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java @@ -87,7 +87,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase public void testPerformUpgrade() throws Exception { UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHostName()); + upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHost()); assertDatabaseRecordCounts(); assertContent(); @@ -101,7 +101,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase corruptDatabase(); UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHost()); assertDatabaseRecordCounts(); @@ -117,7 +117,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase UpgradeInteractionHandler discardMessageInteractionHandler = new StaticAnswerHandler(UpgradeInteractionResponse.NO); - upgrade.performUpgrade(_environment, discardMessageInteractionHandler, getVirtualHostName()); + upgrade.performUpgrade(_environment, discardMessageInteractionHandler, getVirtualHost()); assertDatabaseRecordCount(NEW_METADATA_DB_NAME, 12); assertDatabaseRecordCount(NEW_CONTENT_DB_NAME, 12); @@ -135,7 +135,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase { populateOldXidEntries(environment); UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHostName()); + upgrade.performUpgrade(environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHost()); assertXidEntries(environment); } finally @@ -171,11 +171,11 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase NewRecordImpl[] newDequeues = newTransaction.getDequeues(); assertEquals("Unxpected new enqueus number", 1, newEnqueues.length); NewRecordImpl enqueue = newEnqueues[0]; - assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST1", getVirtualHostName()), enqueue.getId()); + assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST1", getVirtualHost().getName()), enqueue.getId()); assertEquals("Unxpected message id", 1, enqueue.getMessageNumber()); assertEquals("Unxpected new dequeues number", 1, newDequeues.length); NewRecordImpl dequeue = newDequeues[0]; - assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST2", getVirtualHostName()), dequeue.getId()); + assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST2", getVirtualHost().getName()), dequeue.getId()); assertEquals("Unxpected message id", 2, dequeue.getMessageNumber()); } @@ -347,13 +347,13 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase { String exchangeName = (String) deserialized.get(Exchange.NAME); assertNotNull(exchangeName); - assertEquals("Unexpected key", key, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHostName())); + assertEquals("Unexpected key", key, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHost().getName())); } else if (type.equals(Queue.class.getName())) { String queueName = (String) deserialized.get(Queue.NAME); assertNotNull(queueName); - assertEquals("Unexpected key", key, UUIDGenerator.generateQueueUUID(queueName, getVirtualHostName())); + assertEquals("Unexpected key", key, UUIDGenerator.generateQueueUUID(queueName, getVirtualHost().getName())); } else if (type.equals(Binding.class.getName())) { @@ -368,15 +368,15 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase private Map createExpectedQueueBindingMapAndID(String queue, String bindingName, String exchangeName, Map argumentMap, List expectedBindingIDs) { Map expectedQueueBinding = new HashMap(); - expectedQueueBinding.put(Binding.QUEUE, UUIDGenerator.generateQueueUUID(queue, getVirtualHostName()).toString()); + expectedQueueBinding.put(Binding.QUEUE, UUIDGenerator.generateQueueUUID(queue, getVirtualHost().getName()).toString()); expectedQueueBinding.put(Binding.NAME, bindingName); - expectedQueueBinding.put(Binding.EXCHANGE, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHostName()).toString()); + expectedQueueBinding.put(Binding.EXCHANGE, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHost().getName()).toString()); if (argumentMap != null) { expectedQueueBinding.put(Binding.ARGUMENTS, argumentMap); } - expectedBindingIDs.add(UUIDGenerator.generateBindingUUID(exchangeName, queue, bindingName, getVirtualHostName())); + expectedBindingIDs.add(UUIDGenerator.generateBindingUUID(exchangeName, queue, bindingName, getVirtualHost().getName())); return expectedQueueBinding; } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java index 400ac12792..c0de884a28 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java @@ -43,7 +43,7 @@ public class UpgraderFailOnNewerVersionTest extends AbstractUpgradeTestCase public void setUp() throws Exception { super.setUp(); - _upgrader = new Upgrader(_environment, getVirtualHostName()); + _upgrader = new Upgrader(_environment, getVirtualHost()); } private int getStoreVersion() diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java index 3465f3582f..4b9a8d19a8 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java @@ -51,7 +51,7 @@ public class UpgraderTest extends AbstractUpgradeTestCase public void setUp() throws Exception { super.setUp(); - _upgrader = new Upgrader(_environment, getVirtualHostName()); + _upgrader = new Upgrader(_environment, getVirtualHost()); } private int getStoreVersion(Environment environment) @@ -108,7 +108,7 @@ public class UpgraderTest extends AbstractUpgradeTestCase Environment emptyEnvironment = createEnvironment(nonExistentStoreLocation); try { - _upgrader = new Upgrader(emptyEnvironment, getVirtualHostName()); + _upgrader = new Upgrader(emptyEnvironment, getVirtualHost()); _upgrader.upgradeIfNecessary(); List databaseNames = emptyEnvironment.getDatabaseNames(); diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java index bd0411619e..29e9ae19d7 100644 --- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java +++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java @@ -25,7 +25,8 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.UUID; -import org.apache.qpid.server.store.StoreException; + +import org.apache.qpid.server.store.*; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; @@ -41,12 +42,6 @@ import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.v0_8.MessageMetaDataType_0_8; -import org.apache.qpid.server.store.MessageStoreTest; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.StorableMessageMetaData; -import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.Transaction; -import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.transport.DeliveryProperties; import org.apache.qpid.transport.Header; import org.apache.qpid.transport.MessageAcceptMode; @@ -56,6 +51,8 @@ import org.apache.qpid.transport.MessageDeliveryPriority; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.MessageTransfer; +import static org.mockito.Mockito.mock; + /** * Subclass of MessageStoreTest which runs the standard tests from the superclass against * the BDB Store as well as additional tests specific to the BDB store-implementation. @@ -230,7 +227,8 @@ public class BDBMessageStoreTest extends MessageStoreTest messageStore.close(); AbstractBDBMessageStore newStore = new BDBMessageStore(); - newStore.configure(getVirtualHostModel(),true); + newStore.setVirtualHost(getVirtualHostModel()); + newStore.configure(true); newStore.startWithNoRecover(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java index 740ccf8200..7e3e5c9bbe 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.model; import org.apache.qpid.server.model.adapter.AbstractConfiguredObject; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import java.security.AccessControlException; import java.util.Collection; @@ -279,4 +280,7 @@ public interface ConfiguredObject> Class getCategoryClass(); + // TODO - remove this when objects become responsible for their own storage + ConfiguredObjectRecord asObjectRecord(); + } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java index ee10ecfc8b..6a771c4c25 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java @@ -27,6 +27,7 @@ import org.apache.qpid.server.store.MessageStore; import java.security.AccessControlException; import java.util.Collection; import java.util.Map; +import java.util.UUID; @ManagedObject( managesChildren = true ) public interface VirtualHost> extends ConfiguredObject @@ -181,6 +182,8 @@ public interface VirtualHost> extends ConfiguredObject< // TODO - remove this TaskExecutor getTaskExecutor(); + Exchange getExchange(UUID id); + MessageStore getMessageStore(); String getType(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java index 2a12284f1f..5e3ebb86ce 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessControlException; import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.*; import org.apache.qpid.server.model.*; @@ -37,6 +38,7 @@ import org.apache.qpid.server.configuration.updater.SetAttributeTask; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.util.MapValueConverter; import org.apache.qpid.server.util.ServerScopedRuntimeException; @@ -544,6 +546,61 @@ public abstract class AbstractConfiguredObject> im return getClass().getSimpleName() + " [id=" + _id + ", name=" + getName() + "]"; } + public ConfiguredObjectRecord asObjectRecord() + { + return new ConfiguredObjectRecord() + { + @Override + public UUID getId() + { + return AbstractConfiguredObject.this.getId(); + } + + @Override + public String getType() + { + return getCategoryClass().getSimpleName(); + } + + @Override + public Map getAttributes() + { + return Subject.doAs(SecurityManager.getSubjectWithAddedSystemRights(), new PrivilegedAction>() + { + @Override + public Map run() + { + Map actualAttributes = new HashMap(getActualAttributes()); + for(Map.Entry entry : actualAttributes.entrySet()) + { + if(entry.getValue() instanceof ConfiguredObject) + { + entry.setValue(((ConfiguredObject)entry.getValue()).getId()); + } + } + actualAttributes.remove(ID); + return actualAttributes; + } + }); + } + + @Override + public Map getParents() + { + Map parents = new LinkedHashMap(); + for(Class parentClass : Model.getInstance().getParentTypes(getCategoryClass())) + { + ConfiguredObject parent = getParent(parentClass); + if(parent != null) + { + parents.put(parentClass.getSimpleName(), parent.asObjectRecord()); + } + } + return parents; + } + }; + } + @SuppressWarnings("unchecked") @Override public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java index 6dbc2eea85..c9bca357f0 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java @@ -1166,8 +1166,15 @@ public final class VirtualHostAdapter extends AbstractConfiguredObject implements Dur { @Override public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer, - final UUID id, - final Map attributes) + final ConfiguredObjectRecord record) { - final UnresolvedObject obj = createUnresolvedObject(id, getType(), attributes); + final UnresolvedObject obj = createUnresolvedObject(record); UnresolvedDependency[] dependencies = obj.getUnresolvedDependencies(); for(final UnresolvedDependency dependency : dependencies) { @@ -53,7 +52,7 @@ public abstract class AbstractDurableConfiguredObjectRecoverer implements Dur dependency.resolve(o); if(obj.getUnresolvedDependencies().length == 0) { - durableConfigurationRecoverer.resolve(getType(), id, obj.resolve()); + durableConfigurationRecoverer.resolve(getType(), record.getId(), obj.resolve()); } } }); @@ -61,17 +60,15 @@ public abstract class AbstractDurableConfiguredObjectRecoverer implements Dur } if(obj.getUnresolvedDependencies().length == 0) { - durableConfigurationRecoverer.resolve(getType(), id, obj.resolve()); + durableConfigurationRecoverer.resolve(getType(), record.getId(), obj.resolve()); } else { - durableConfigurationRecoverer.addUnresolvedObject(getType(), id, obj); + durableConfigurationRecoverer.addUnresolvedObject(getType(), record.getId(), obj); } } - public abstract UnresolvedObject createUnresolvedObject(final UUID id, - final String type, - final Map attributes); + public abstract UnresolvedObject createUnresolvedObject(final ConfiguredObjectRecord record); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java index 81c74fe8f5..01b142c9bc 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java @@ -24,6 +24,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.ref.SoftReference; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -34,12 +36,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; @@ -48,6 +45,7 @@ import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.plugin.MessageMetaDataType; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.transport.ConnectionOpen; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParseException; @@ -70,19 +68,21 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; - private static final String LINKS_TABLE_NAME = "QPID_LINKS"; - private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; private static final String XID_TABLE_NAME = "QPID_XIDS"; private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; + private static final String CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME = "QPID_CONFIGURED_OBJECT_HIERARCHY"; + private static final int DEFAULT_CONFIG_VERSION = 0; - public static String[] ALL_TABLES = new String[] { DB_VERSION_TABLE_NAME, LINKS_TABLE_NAME, BRIDGES_TABLE_NAME, XID_ACTIONS_TABLE_NAME, - XID_TABLE_NAME, QUEUE_ENTRY_TABLE_NAME, MESSAGE_CONTENT_TABLE_NAME, META_DATA_TABLE_NAME, CONFIGURED_OBJECTS_TABLE_NAME, CONFIGURATION_VERSION_TABLE_NAME }; + public static String[] ALL_TABLES = + new String[]{DB_VERSION_TABLE_NAME, XID_ACTIONS_TABLE_NAME, + XID_TABLE_NAME, QUEUE_ENTRY_TABLE_NAME, MESSAGE_CONTENT_TABLE_NAME, META_DATA_TABLE_NAME, + CONFIGURED_OBJECTS_TABLE_NAME, CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME, CONFIGURATION_VERSION_TABLE_NAME}; - private static final int DB_VERSION = 7; + private static final int DB_VERSION = 8; private final AtomicLong _messageId = new AtomicLong(0); private final AtomicBoolean _closed = new AtomicBoolean(false); @@ -115,34 +115,6 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; - private static final String SELECT_FROM_LINKS = - "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; - private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " - + "arguments FROM " + LINKS_TABLE_NAME; - private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" - + " id_msb = ?"; - private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " - + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; - private static final String SELECT_FROM_BRIDGES = - "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " - + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " - + " create_time," - + " link_id_lsb, link_id_msb, " - + "arguments FROM " + BRIDGES_TABLE_NAME - + " WHERE link_id_lsb = ? and link_id_msb = ?"; - private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + - " WHERE id_lsb = ? and id_msb = ?"; - private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " - + "create_time, " - + "link_id_lsb, link_id_msb, " - + "arguments )" - + " values (?, ?, ?, ?, ?, ?)"; - private static final String INSERT_INTO_XIDS = "INSERT INTO "+ XID_TABLE_NAME +" ( format, global_id, branch_id ) values (?, ?, ?)"; private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME @@ -166,6 +138,14 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC + " where id = ?"; private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; + + private static final String INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY = "INSERT INTO " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " ( child_id, parent_type, parent_id) VALUES (?,?,?)"; + + private static final String DELETE_FROM_CONFIGURED_OBJECT_HIERARCHY = "DELETE FROM " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " where child_id = ?"; + private static final String SELECT_FROM_CONFIGURED_OBJECT_HIERARCHY = "SELECT child_id, parent_type, parent_id FROM " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME; + protected static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); @@ -319,6 +299,8 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { case 6: upgradeFromV6(); + case 7: + upgradeFromV7(); case DB_VERSION: return; default: @@ -347,6 +329,135 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC updateDbVersion(7); } + + private void upgradeFromV7() throws SQLException + { + Connection connection = newConnection(); + try + { + Map> bindingsToUpdate = new HashMap>(); + List others = new ArrayList(); + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(_module); + + PreparedStatement stmt = connection.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + UUID id = UUID.fromString(rs.getString(1)); + String objectType = rs.getString(2); + Map attributes = objectMapper.readValue(getBlobAsString(rs, 3),Map.class); + if(objectType.endsWith("Binding")) + { + bindingsToUpdate.put(id,attributes); + } + else + { + others.add(id); + } + } + } + catch (JsonMappingException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (JsonParseException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + stmt = connection.prepareStatement(INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY); + try + { + for (UUID id : others) + { + stmt.setString(1, id.toString()); + stmt.setString(2, "VirtualHost"); + stmt.setString(3, _virtualHost.getId().toString()); + stmt.execute(); + } + for(Map.Entry> bindingEntry : bindingsToUpdate.entrySet()) + { + stmt.setString(1, bindingEntry.getKey().toString()); + stmt.setString(2,"Queue"); + stmt.setString(3, bindingEntry.getValue().remove("queue").toString()); + stmt.execute(); + + stmt.setString(1, bindingEntry.getKey().toString()); + stmt.setString(2,"Exchange"); + stmt.setString(3, bindingEntry.getValue().remove("exchange").toString()); + stmt.execute(); + } + } + finally + { + stmt.close(); + } + stmt = connection.prepareStatement(UPDATE_CONFIGURED_OBJECTS); + try + { + for(Map.Entry> bindingEntry : bindingsToUpdate.entrySet()) + { + stmt.setString(1, "Binding"); + byte[] attributesAsBytes = objectMapper.writeValueAsBytes(bindingEntry.getValue()); + + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + stmt.setBinaryStream(2, bis, attributesAsBytes.length); + stmt.setString(3, bindingEntry.getKey().toString()); + stmt.execute(); + } + } + catch (JsonMappingException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (JsonGenerationException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + finally + { + stmt.close(); + } + stmt = connection.prepareStatement(UPDATE_DB_VERSION); + try + { + stmt.setInt(1, 8); + stmt.execute(); + } + finally + { + stmt.close(); + } + connection.commit(); + } + finally + { + connection.close(); + } + } + private void updateDbVersion(int newVersion) throws SQLException { Connection conn = newAutoCommitConnection(); @@ -388,11 +499,10 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC createVersionTable(conn); createConfigVersionTable(conn); createConfiguredObjectsTable(conn); + createConfiguredObjectHierarchyTable(conn); createQueueEntryTable(conn); createMetaDataTable(conn); createMessageContentTable(conn); - createLinkTable(conn); - createBridgeTable(conn); createXidTable(conn); createXidActionTable(conn); conn.close(); @@ -469,6 +579,23 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } } + private void createConfiguredObjectHierarchyTable(final Connection conn) throws SQLException + { + if(!tableExists(CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " ( child_id VARCHAR(36) not null, parent_type varchar(255), parent_id VARCHAR(36), PRIMARY KEY (child_id, parent_type))"); + } + finally + { + stmt.close(); + } + } + } + private void createQueueEntryTable(final Connection conn) throws SQLException @@ -535,45 +662,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } - private void createLinkTable(final Connection conn) throws SQLException - { - if(!tableExists(LINKS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute("CREATE TABLE "+ LINKS_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," - + " id_msb " + getSqlBigIntType() + " not null," - + " create_time " + getSqlBigIntType() + " not null," - + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); - } - finally - { - stmt.close(); - } - } - } - private void createBridgeTable(final Connection conn) throws SQLException - { - if(!tableExists(BRIDGES_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute("CREATE TABLE "+ BRIDGES_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," - + " id_msb " + getSqlBigIntType() + " not null," - + " create_time " + getSqlBigIntType() + " not null," - + " link_id_lsb " + getSqlBigIntType() + " not null," - + " link_id_msb " + getSqlBigIntType() + " not null," - + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); - } - finally - { - stmt.close(); - } - } - } private void createXidTable(final Connection conn) throws SQLException { @@ -817,35 +906,26 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC @Override - public void create(UUID id, String type, Map attributes) throws StoreException + public void create(ConfiguredObjectRecord object) throws StoreException { if (_stateManager.isInState(State.ACTIVE)) { - insertConfiguredObject(new ConfiguredObjectRecord(id, type, attributes)); - } - - } - - @Override - public void remove(UUID id, String type) throws StoreException - { - int results = removeConfiguredObject(id); - if (results == 0) - { - throw new StoreException(type + " with id " + id + " not found"); - } - } - - @Override - public void update(UUID id, String type, Map attributes) throws StoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(id); - if (queueConfiguredObject != null) + try { - ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes); - updateConfiguredObject(newQueueRecord); + Connection conn = newConnection(); + try + { + insertConfiguredObject(object, conn); + conn.commit(); + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new StoreException("Error creating ConfiguredObject " + object); } } @@ -1993,109 +2073,85 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC _eventManager.addEventListener(eventListener, events); } - private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws StoreException + private void insertConfiguredObject(ConfiguredObjectRecord configuredObject, final Connection conn) throws StoreException { - if (_stateManager.isInState(State.ACTIVE)) + try { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); try { - Connection conn = newAutoCommitConnection(); + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + boolean exists; try { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + exists = rs.next(); + + } + finally + { + rs.close(); + } + // If we don't have any data in the result set then we can add this configured object + if (!exists) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); try { - stmt.setString(1, configuredObject.getId().toString()); - ResultSet rs = stmt.executeQuery(); - try + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, configuredObject.getType()); + if(configuredObject.getAttributes() == null) { - // If we don't have any data in the result set then we can add this configured object - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); - try - { - insertStmt.setString(1, configuredObject.getId().toString()); - insertStmt.setString(2, configuredObject.getType()); - if(configuredObject.getAttributes() == null) - { - insertStmt.setNull(3, Types.BLOB); - } - else - { - final Map attributes = configuredObject.getAttributes(); - final ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(_module); - byte[] attributesAsBytes = objectMapper.writeValueAsBytes(attributes); - - ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); - insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); - } - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } + insertStmt.setNull(3, Types.BLOB); } - finally + else { - rs.close(); + final Map attributes = configuredObject.getAttributes(); + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(_module); + byte[] attributesAsBytes = objectMapper.writeValueAsBytes(attributes); + + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); } + insertStmt.execute(); } finally { - stmt.close(); + insertStmt.close(); } + + writeHierarchy(configuredObject, conn); } - finally - { - conn.close(); - } - } - catch (JsonMappingException e) - { - throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); - } - catch (JsonGenerationException e) - { - throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); - } - catch (IOException e) - { - throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); - } - catch (SQLException e) - { - throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); - } - } - } - private int removeConfiguredObject(UUID id) throws StoreException - { - int results = 0; - try - { - Connection conn = newAutoCommitConnection(); - try - { - results = removeConfiguredObject(id, conn); } finally { - conn.close(); + stmt.close(); } + + } + catch (JsonMappingException e) + { + throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); + } + catch (JsonGenerationException e) + { + throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); } catch (SQLException e) { - throw new StoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); + throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); } - return results; + } - public UUID[] removeConfiguredObjects(UUID... objects) throws StoreException + @Override + public UUID[] remove(ConfiguredObjectRecord... objects) throws StoreException { Collection removed = new ArrayList(objects.length); try @@ -2104,11 +2160,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC Connection conn = newAutoCommitConnection(); try { - for(UUID id : objects) + for(ConfiguredObjectRecord record : objects) { - if(removeConfiguredObject(id, conn) != 0) + if(removeConfiguredObject(record.getId(), conn) != 0) { - removed.add(id); + removed.add(record.getId()); } } } @@ -2126,7 +2182,8 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private int removeConfiguredObject(final UUID id, final Connection conn) throws SQLException { - final int results;PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); + final int results; + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); try { stmt.setString(1, id.toString()); @@ -2136,30 +2193,18 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { stmt.close(); } - return results; - } - - private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws StoreException - { - if (_stateManager.isInState(State.ACTIVE)) + stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECT_HIERARCHY); + try { - try - { - Connection conn = newAutoCommitConnection(); - try - { - updateConfiguredObject(configuredObject, false, conn); - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new StoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); - } + stmt.setString(1, id.toString()); + stmt.executeUpdate(); } + finally + { + stmt.close(); + } + + return results; } public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException @@ -2254,6 +2299,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { insertStmt.close(); } + writeHierarchy(configuredObject, conn); } } finally @@ -2280,72 +2326,31 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } - private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws StoreException + private void writeHierarchy(final ConfiguredObjectRecord configuredObject, final Connection conn) throws SQLException, StoreException { - ConfiguredObjectRecord result = null; + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY); try { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); - try - { - stmt.setString(1, id.toString()); - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - String type = rs.getString(1); - String attributes = getBlobAsString(rs, 2); - result = new ConfiguredObjectRecord(id, type, - (new ObjectMapper()).readValue(attributes,Map.class)); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally + for(Map.Entry parentEntry : configuredObject.getParents().entrySet()) { - conn.close(); + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, parentEntry.getKey()); + insertStmt.setString(3, parentEntry.getValue().getId().toString()); + + insertStmt.execute(); } } - catch (JsonMappingException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (JsonParseException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (IOException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (SQLException e) + finally { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); + insertStmt.close(); } - return result; } private void loadConfiguredObjects(ConfigurationRecoveryHandler recoveryHandler) throws SQLException, StoreException { Connection conn = newAutoCommitConnection(); - + Map configuredObjects = new HashMap(); final ObjectMapper objectMapper = new ObjectMapper(); try { @@ -2360,8 +2365,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC String id = rs.getString(1); String objectType = rs.getString(2); String attributes = getBlobAsString(rs, 3); - recoveryHandler.configuredObject(UUID.fromString(id), objectType, - objectMapper.readValue(attributes,Map.class)); + final ConfiguredObjectRecordImpl configuredObjectRecord = + new ConfiguredObjectRecordImpl(UUID.fromString(id), objectType, + objectMapper.readValue(attributes, Map.class)); + configuredObjects.put(configuredObjectRecord.getId(),configuredObjectRecord); + } } catch (JsonMappingException e) @@ -2385,11 +2393,53 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { stmt.close(); } + stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECT_HIERARCHY); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + UUID childId = UUID.fromString(rs.getString(1)); + String parentType = rs.getString(2); + UUID parentId = UUID.fromString(rs.getString(3)); + + ConfiguredObjectRecordImpl child = configuredObjects.get(childId); + ConfiguredObjectRecordImpl parent = configuredObjects.get(parentId); + + if(child != null && parent != null) + { + child.addParent(parentType, parent); + } + else if(child != null && child.getType().endsWith("Binding") && parentType.equals("Exchange")) + { + // TODO - remove this hack for amq. exchanges + child.addParent(parentType, new ConfiguredObjectRecordImpl(parentId, parentType, Collections.emptyMap())); + } + + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } finally { conn.close(); } + + for(ConfiguredObjectRecord record : configuredObjects.values()) + { + recoveryHandler.configuredObject(record); + } } protected abstract String getBlobAsString(ResultSet rs, int col) throws SQLException; @@ -2429,4 +2479,51 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } } + + private static final class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord + { + + private final UUID _id; + private final String _type; + private final Map _attributes; + private final Map _parents = new HashMap(); + + private ConfiguredObjectRecordImpl(final UUID id, + final String type, + final Map attributes) + { + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(attributes); + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + private void addParent(String parentType, ConfiguredObjectRecord parent) + { + _parents.put(parentType, parent); + } + + @Override + public Map getAttributes() + { + return _attributes; + } + + @Override + public Map getParents() + { + return Collections.unmodifiableMap(_parents); + } + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java index c6ebe90802..c8aef92a95 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java @@ -27,7 +27,7 @@ public interface ConfigurationRecoveryHandler { void beginConfigurationRecovery(DurableConfigurationStore store, int configVersion); - void configuredObject(UUID id, String type, Map attributes); + void configuredObject(ConfiguredObjectRecord object); /** * diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java index 44490385d9..99f8f0f04a 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java @@ -20,69 +20,16 @@ */ package org.apache.qpid.server.store; -import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; -public class ConfiguredObjectRecord +public interface ConfiguredObjectRecord { - private UUID _id; - private String _type; - private Map _attributes; + UUID getId(); - public ConfiguredObjectRecord(UUID id, String type, Map attributes) - { - super(); - _id = id; - _type = type; - _attributes = Collections.unmodifiableMap(new LinkedHashMap(attributes)); - } + String getType(); - public UUID getId() - { - return _id; - } + Map getAttributes(); - public String getType() - { - return _type; - } - - public Map getAttributes() - { - return _attributes; - } - - @Override - public String toString() - { - return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; - } - - @Override - public boolean equals(Object o) - { - if(this == o) - { - return true; - } - if(o == null || getClass() != o.getClass()) - { - return false; - } - - ConfiguredObjectRecord that = (ConfiguredObjectRecord) o; - - return _type.equals(that._type) && _id.equals(that._id) && _attributes.equals(that._attributes); - } - - @Override - public int hashCode() - { - int result = _id.hashCode(); - result = 31 * result + _type.hashCode(); - result = 31 * result + _attributes.hashCode(); - return result; - } + Map getParents(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java new file mode 100644 index 0000000000..ed553aa823 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.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.server.store; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord +{ + private UUID _id; + private String _type; + private final Map _attributes; + private final Map _parents; + + public ConfiguredObjectRecordImpl(UUID id, String type, Map attributes) + { + this(id,type,attributes,Collections.emptyMap()); + } + + public ConfiguredObjectRecordImpl(UUID id, String type, Map attributes, Map parents) + { + super(); + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(new LinkedHashMap(attributes)); + _parents = Collections.unmodifiableMap(new LinkedHashMap(parents)); + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + @Override + public Map getAttributes() + { + return _attributes; + } + + @Override + public Map getParents() + { + return _parents; + } + + @Override + public String toString() + { + return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + ConfiguredObjectRecordImpl that = (ConfiguredObjectRecordImpl) o; + + return _type.equals(that._type) && _id.equals(that._id) && _attributes.equals(that._attributes); + } + + @Override + public int hashCode() + { + int result = _id.hashCode(); + result = 31 * result + _type.hashCode(); + result = 31 * result + _attributes.hashCode(); + return result; + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java index 6696426c0e..f8d8ecdd7c 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.store; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; @@ -76,19 +77,19 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl } @Override - public void configuredObject(final UUID id, final String type, final Map attributes) + public void configuredObject(ConfiguredObjectRecord record) { - _upgrader.configuredObject(id, type, attributes); + _upgrader.configuredObject(record); } - void onConfiguredObject(final UUID id, final String type, final Map attributes) + void onConfiguredObject(ConfiguredObjectRecord record) { - DurableConfiguredObjectRecoverer recoverer = getRecoverer(type); + DurableConfiguredObjectRecoverer recoverer = getRecoverer(record.getType()); if(recoverer == null) { - throw new IllegalConfigurationException("Unknown type for configured object: " + type); + throw new IllegalConfigurationException("Unknown type for configured object: " + record.getType()); } - recoverer.load(this, id, attributes); + recoverer.load(this, record); } private DurableConfiguredObjectRecoverer getRecoverer(final String type) @@ -111,19 +112,12 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl private void applyUpgrade() { - final Collection updates = new ArrayList(); - final Collection deletes = new ArrayList(); - for(Map.Entry entry : _upgrader.getUpdatedRecords().entrySet()) - { - if(entry.getValue() != null) - { - updates.add(entry.getValue()); - } - else - { - deletes.add(entry.getKey()); - } - } + final Collection updates = new HashSet(_upgrader.getUpdatedRecords().values()); + final Collection deletes = new HashSet(_upgrader.getDeletedRecords().values()); + + // Due to the way the upgraders work it is possible that the updates list may contain nulls + updates.remove(null); + deletes.remove(null); if(!updates.isEmpty()) { @@ -131,7 +125,7 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl } if(!deletes.isEmpty()) { - _store.removeConfiguredObjects(deletes.toArray(new UUID[deletes.size()])); + _store.remove(deletes.toArray(new ConfiguredObjectRecord[deletes.size()])); } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java index 589eca1600..ae138f12c2 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java @@ -50,41 +50,33 @@ public interface DurableConfigurationStore /** * Makes the specified object persistent. * - * @param id The id of the object to persist. - * @param type The type of the object to persist - * @param attributes the attributes of the object to persist + * @param object The object to persist. * * @throws StoreException If the operation fails for any reason. */ - void create(UUID id, String type, Map attributes) throws StoreException; + void create(ConfiguredObjectRecord object) throws StoreException; /** - * Removes the specified persistent configured object. + * Removes the specified persistent configured objects. * - * @param id The id of the object to remove. - * @param type The type of the object to remove + * @param objects The objects to remove. * * @throws StoreException If the operation fails for any reason. */ - void remove(UUID id, String type) throws StoreException; - - public UUID[] removeConfiguredObjects(UUID... objects) throws StoreException; + public UUID[] remove(ConfiguredObjectRecord... objects) throws StoreException; /** - * Updates the specified object in the persistent store, IF it is already present. If the object - * is not present in the store, it will not be added. + * Updates the specified objects in the persistent store, IF it is already present. If the object + * is not present in the store, it will only be added if createIfNecessary is set to true, otherwise an exception + * will be thrown. * - * @param id The id of the object to update. - * @param type The type of the object to update - * @param attributes the updated attributes + * @param createIfNecessary if false then will fail if the object does not exist. + * @param records the records to update * * @throws StoreException If the operation fails for any reason. */ - void update(UUID id, String type, Map attributes) throws StoreException; - - - public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException; + void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException; void close() throws Exception; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java index f914389b0e..9410006d65 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java @@ -20,12 +20,8 @@ */ package org.apache.qpid.server.store; -import java.security.PrivilegedAction; import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -35,12 +31,8 @@ import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.model.Binding; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Exchange; -import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.*; - -import javax.security.auth.Subject; public class DurableConfigurationStoreHelper { @@ -58,7 +50,7 @@ public class DurableConfigurationStoreHelper { attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); } - store.update(queue.getId(), QUEUE, attributesMap); + store.update(false, new ConfiguredObjectRecordImpl(queue.getId(), QUEUE, attributesMap)); } public static void createQueue(DurableConfigurationStore store, AMQQueue queue) @@ -71,49 +63,33 @@ public class DurableConfigurationStoreHelper attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); } - store.create(queue.getId(), QUEUE, attributesMap); + store.create(new ConfiguredObjectRecordImpl(queue.getId(), QUEUE, attributesMap)); } public static void removeQueue(DurableConfigurationStore store, AMQQueue queue) { - store.remove(queue.getId(), QUEUE); + store.remove(queue.asObjectRecord()); } public static void createExchange(DurableConfigurationStore store, ExchangeImpl exchange) { - Map attributesMap = exchange.getActualAttributes(); - attributesMap.remove(ConfiguredObject.ID); - - store.create(exchange.getId(), EXCHANGE, attributesMap); - + store.create(exchange.asObjectRecord()); } - public static void removeExchange(DurableConfigurationStore store, ExchangeImpl exchange) { - store.remove(exchange.getId(), EXCHANGE); + store.remove(exchange.asObjectRecord()); } public static void createBinding(DurableConfigurationStore store, final BindingImpl binding) { - Map attributesMap = binding.getActualAttributes(); - attributesMap.remove(ConfiguredObject.ID); - if(!attributesMap.containsKey(Binding.EXCHANGE)) - { - attributesMap.put(Binding.EXCHANGE, binding.getExchange()); - } - if(!attributesMap.containsKey(Binding.QUEUE)) - { - attributesMap.put(Binding.QUEUE, binding.getQueue()); - } - - store.create(binding.getId(), BINDING, attributesMap); + store.create(binding.asObjectRecord()); } public static void removeBinding(DurableConfigurationStore store, BindingImpl binding) { - store.remove(binding.getId(), BINDING); + store.remove(binding.asObjectRecord()); } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java index 1d3e4cc672..45b2ff56d7 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java @@ -25,11 +25,12 @@ import java.util.UUID; public interface DurableConfigurationStoreUpgrader { - void configuredObject(UUID id, String type, Map attributes); + void configuredObject(ConfiguredObjectRecord record); void complete(); void setNextUpgrader(DurableConfigurationStoreUpgrader upgrader); Map getUpdatedRecords(); + Map getDeletedRecords(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java index e065728bd3..4c618f5089 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java @@ -26,8 +26,7 @@ import java.util.UUID; public interface DurableConfiguredObjectRecoverer { public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer, - final UUID id, - final Map attributes); + final ConfiguredObjectRecord record); public String getType(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java index e99c2019cd..c8898eedb6 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java @@ -26,14 +26,7 @@ import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Model; @@ -106,7 +99,7 @@ public class JsonFileConfigStore implements DurableConfigurationStore List records = new ArrayList(_objectsById.values()); for(ConfiguredObjectRecord record : records) { - recoveryHandler.configuredObject(record.getId(), record.getType(), record.getAttributes()); + recoveryHandler.configuredObject(record); } int oldConfigVersion = _configVersion; _configVersion = recoveryHandler.completeConfigurationRecovery(); @@ -311,20 +304,21 @@ public class JsonFileConfigStore implements DurableConfigurationStore } } + Map parentMap = new HashMap(); if(parentId != null) { - data.put(parentClass.getSimpleName().toLowerCase(),parentId); + parentMap.put(parentClass.getSimpleName(),parentId); for(Class otherParent : MODEL.getParentTypes(clazz)) { if(otherParent != parentClass) { final String otherParentAttr = otherParent.getSimpleName().toLowerCase(); - Object otherParentId = data.get(otherParentAttr); + Object otherParentId = data.remove(otherParentAttr); if(otherParentId instanceof String) { try { - data.put(otherParentAttr, UUID.fromString((String) otherParentId)); + parentMap.put(otherParent.getSimpleName(), UUID.fromString((String) otherParentId)); } catch(IllegalArgumentException e) { @@ -336,7 +330,7 @@ public class JsonFileConfigStore implements DurableConfigurationStore } } - _objectsById.put(id, new ConfiguredObjectRecord(id, type, data)); + _objectsById.put(id, new ConfiguredObjectRecordImpl(id, type, data, parentMap)); List idsForType = _idsByType.get(type); if(idsForType == null) { @@ -348,28 +342,27 @@ public class JsonFileConfigStore implements DurableConfigurationStore } @Override - public synchronized void create(final UUID id, final String type, final Map attributes) throws - StoreException + public synchronized void create(ConfiguredObjectRecord record) throws StoreException { - if(_objectsById.containsKey(id)) + if(_objectsById.containsKey(record.getId())) { - throw new StoreException("Object with id " + id + " already exists"); + throw new StoreException("Object with id " + record.getId() + " already exists"); } - else if(!CLASS_NAME_MAPPING.containsKey(type)) + else if(!CLASS_NAME_MAPPING.containsKey(record.getType())) { - throw new StoreException("Cannot create object of unknown type " + type); + throw new StoreException("Cannot create object of unknown type " + record.getType()); } else { - ConfiguredObjectRecord record = new ConfiguredObjectRecord(id, type, attributes); - _objectsById.put(id, record); - List idsForType = _idsByType.get(type); + + _objectsById.put(record.getId(), record); + List idsForType = _idsByType.get(record.getType()); if(idsForType == null) { idsForType = new ArrayList(); - _idsByType.put(type, idsForType); + _idsByType.put(record.getType(), idsForType); } - idsForType.add(id); + idsForType.add(record.getId()); save(); } } @@ -424,7 +417,21 @@ public class JsonFileConfigStore implements DurableConfigurationStore Map map = new LinkedHashMap(); map.put("id", id); map.putAll(record.getAttributes()); - map.remove(MODEL.getParentTypes(type).iterator().next().getSimpleName().toLowerCase()); + + Collection> parentTypes = MODEL.getParentTypes(type); + if(parentTypes.size() > 1) + { + Iterator> iter = parentTypes.iterator(); + // skip the first parent, which is given by structure + iter.next(); + // for all other parents add a fake attribute with name being the parent type in lower case, and the value + // being the parents id + while(iter.hasNext()) + { + String parentType = iter.next().getSimpleName(); + map.put(parentType.toLowerCase(), record.getParents().get(parentType).getId()); + } + } Collection> childClasses = new ArrayList>(MODEL.getChildTypes(type)); @@ -442,17 +449,9 @@ public class JsonFileConfigStore implements DurableConfigurationStore for(UUID childId : childIds) { ConfiguredObjectRecord childRecord = _objectsById.get(childId); - final String parentArg = type.getSimpleName().toLowerCase(); - final Object parent = childRecord.getAttributes().get(parentArg); - String parentId; - if(parent instanceof ConfiguredObject) - { - parentId = ((ConfiguredObject)parent).getId().toString(); - } - else - { - parentId = String.valueOf(parent); - } + + final ConfiguredObjectRecord parent = childRecord.getParents().get(type.getSimpleName()); + String parentId = parent.getId().toString(); if(id.toString().equals(parentId)) { entities.add(build(childClass,childId)); @@ -470,34 +469,22 @@ public class JsonFileConfigStore implements DurableConfigurationStore } @Override - public void remove(final UUID id, final String type) throws StoreException - { - removeConfiguredObjects(id); - } - - @Override - public synchronized UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException + public synchronized UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { List removedIds = new ArrayList(); - for(UUID id : objects) + for(ConfiguredObjectRecord requestedRecord : objects) { - ConfiguredObjectRecord record = _objectsById.remove(id); + ConfiguredObjectRecord record = _objectsById.remove(requestedRecord.getId()); if(record != null) { - removedIds.add(id); - _idsByType.get(record.getType()).remove(id); + removedIds.add(record.getId()); + _idsByType.get(record.getType()).remove(record.getId()); } } save(); return removedIds.toArray(new UUID[removedIds.size()]); } - @Override - public void update(final UUID id, final String type, final Map attributes) throws - StoreException - { - update(false, new ConfiguredObjectRecord(id, type, attributes)); - } @Override public void update(final boolean createIfNecessary, final ConfiguredObjectRecord... records) @@ -583,5 +570,59 @@ public class JsonFileConfigStore implements DurableConfigurationStore return map; } + private class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord + { + + private final UUID _id; + private final String _type; + private final Map _attributes; + private final Map _parents; + + private ConfiguredObjectRecordImpl(final UUID id, final String type, final Map attributes, + final Map parents) + { + _id = id; + _type = type; + _attributes = attributes; + _parents = parents; + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + @Override + public Map getAttributes() + { + return _attributes; + } + + @Override + public Map getParents() + { + Map parents = new HashMap(); + for(Map.Entry entry : _parents.entrySet()) + { + ConfiguredObjectRecord value = _objectsById.get(entry.getValue()); + + if(value == null && entry.getKey().equals("Exchange")) + { + // TODO - remove this hack for the defined exchanges + value = new ConfiguredObjectRecordImpl(entry.getValue(),entry.getKey(),Collections.emptyMap(), Collections.emptyMap()); + } + + parents.put(entry.getKey(), value); + } + return parents; + } + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java index a671e93b26..1abd3de34a 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java @@ -28,6 +28,7 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad { private DurableConfigurationStoreUpgrader _nextUpgrader; private final Map _updates = new HashMap(); + private final Map _deletes = new HashMap(); public final void setNextUpgrader(final DurableConfigurationStoreUpgrader upgrader) { @@ -50,6 +51,10 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad { return _updates; } + protected Map getDeleteMap() + { + return _deletes; + } @Override public final Map getUpdatedRecords() @@ -59,4 +64,11 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad return updates; } + @Override + public final Map getDeletedRecords() + { + final Map deletes = new HashMap(_deletes); + deletes.putAll(_nextUpgrader.getDeletedRecords()); + return deletes; + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java index c579a27731..782ac40a5d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java @@ -19,7 +19,6 @@ */ package org.apache.qpid.server.store; -import java.util.Map; import java.util.UUID; import org.apache.qpid.server.model.VirtualHost; @@ -31,30 +30,24 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura { } - @Override - public void update(UUID id, String type, Map attributes) - { - } - @Override public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) { } - - @Override - public void remove(UUID id, String type) - { - } - @Override - public UUID[] removeConfiguredObjects(final UUID... objects) + public UUID[] remove(final ConfiguredObjectRecord... objects) { - return objects; + final UUID[] removed = new UUID[objects.length]; + for(int i = 0; i < objects.length; i++) + { + removed[i] = objects[i].getId(); + } + return removed; } @Override - public void create(UUID id, String type, Map attributes) + public void create(ConfiguredObjectRecord record) { } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java index c8a812fa89..b17dd4a6f3 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java @@ -34,9 +34,9 @@ public final class NullUpgrader implements DurableConfigurationStoreUpgrader } @Override - public void configuredObject(final UUID id, final String type, final Map attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - _durableConfigurationRecoverer.onConfiguredObject(id, type, attributes); + _durableConfigurationRecoverer.onConfiguredObject(record); } @Override @@ -55,4 +55,10 @@ public final class NullUpgrader implements DurableConfigurationStoreUpgrader { return Collections.emptyMap(); } + + @Override + public Map getDeletedRecords() + { + return Collections.emptyMap(); + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java index e6577e04e4..58721ea8b2 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java @@ -28,9 +28,11 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; @@ -49,11 +51,9 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer createUnresolvedObject(final UUID id, - final String type, - final Map attributes) + public UnresolvedObject createUnresolvedObject(ConfiguredObjectRecord record) { - return new UnresolvedBinding(id, attributes); + return new UnresolvedBinding(record); } @Override @@ -76,12 +76,11 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer attributeMap) + public UnresolvedBinding(final ConfiguredObjectRecord record) { - _bindingId = id; - _exchangeId = UUID.fromString(String.valueOf(attributeMap.get(org.apache.qpid.server.model.Binding.EXCHANGE))); - _queueId = UUID.fromString(String.valueOf(attributeMap.get(org.apache.qpid.server.model.Binding.QUEUE))); + _bindingId = record.getId(); + _exchangeId = record.getParents().get(Exchange.class.getSimpleName()).getId(); + _queueId = record.getParents().get(Queue.class.getSimpleName()).getId(); _exchange = _exchangeRegistry.getExchange(_exchangeId); if(_exchange == null) { @@ -93,8 +92,8 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer) attributeMap.get(org.apache.qpid.server.model.Binding.ARGUMENTS); + _bindingName = (String) record.getAttributes().get(org.apache.qpid.server.model.Binding.NAME); + _bindingArgumentsMap = (Map) record.getAttributes().get(org.apache.qpid.server.model.Binding.ARGUMENTS); } @Override diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java index efdca3b67d..6718acfccf 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.QueueArgumentsConverter; import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; import org.apache.qpid.server.store.DurableConfigurationRecoverer; import org.apache.qpid.server.store.DurableConfigurationStoreUpgrader; import org.apache.qpid.server.store.NonNullUpgrader; @@ -105,9 +106,9 @@ public class DefaultUpgraderProvider implements UpgraderProvider } @Override - public void configuredObject(final UUID id, final String type, Map attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - _records.put(id, new ConfiguredObjectRecord(id, type, attributes)); + _records.put(record.getId(), record); } private void removeSelectorArguments(Map binding) @@ -119,9 +120,9 @@ public class DefaultUpgraderProvider implements UpgraderProvider binding.put(Binding.ARGUMENTS, arguments); } - private boolean isTopicExchange(Map binding) + private boolean isTopicExchange(ConfiguredObjectRecord entry) { - UUID exchangeId = UUID.fromString((String)binding.get(Binding.EXCHANGE)); + UUID exchangeId = entry.getParents().get("Exchange").getId(); if(_records.containsKey(exchangeId)) { @@ -155,17 +156,17 @@ public class DefaultUpgraderProvider implements UpgraderProvider String type = record.getType(); Map attributes = record.getAttributes(); UUID id = record.getId(); - if(type.equals(Binding.class.getName()) && hasSelectorArguments(attributes) && !isTopicExchange(attributes)) + if(type.equals(Binding.class.getName()) && hasSelectorArguments(attributes) && !isTopicExchange(record)) { attributes = new LinkedHashMap(attributes); removeSelectorArguments(attributes); - record = new ConfiguredObjectRecord(id, type, attributes); + record = new ConfiguredObjectRecordImpl(id, type, attributes, record.getParents()); getUpdateMap().put(id, record); entry.setValue(record); } - getNextUpgrader().configuredObject(id, type, attributes); + getNextUpgrader().configuredObject(record); } getNextUpgrader().complete(); @@ -179,12 +180,14 @@ public class DefaultUpgraderProvider implements UpgraderProvider */ private class Version1Upgrader extends NonNullUpgrader { + + @Override - public void configuredObject(final UUID id, String type, final Map attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - type = type.substring(1+type.lastIndexOf('.')); - getUpdateMap().put(id, new ConfiguredObjectRecord(id, type, attributes)); - + String type = record.getType().substring(1 + record.getType().lastIndexOf('.')); + getUpdateMap().put(record.getId(), + new ConfiguredObjectRecordImpl(record.getId(), type, record.getAttributes(), record.getParents())); } @Override @@ -193,30 +196,31 @@ public class DefaultUpgraderProvider implements UpgraderProvider for(Map.Entry entry : getUpdateMap().entrySet()) { final ConfiguredObjectRecord record = entry.getValue(); - if(isBinding(record.getType()) && (unknownExchange((String) record.getAttributes().get(Binding.EXCHANGE)) - || unknownQueue((String) record.getAttributes().get(Binding.QUEUE)))) + final ConfiguredObjectRecord exchangeParent = record.getParents().get(Exchange.class.getSimpleName()); + final ConfiguredObjectRecord queueParent = record.getParents().get(Queue.class.getSimpleName()); + if(isBinding(record.getType()) && (exchangeParent == null || unknownExchange(exchangeParent.getId()) + || queueParent == null || unknownQueue(queueParent.getId()))) { + getDeleteMap().put(entry.getKey(), entry.getValue()); entry.setValue(null); } else { - getNextUpgrader().configuredObject(record.getId(), record.getType(), record.getAttributes()); + getNextUpgrader().configuredObject(record); } } getNextUpgrader().complete(); } - private boolean unknownExchange(final String exchangeIdString) + private boolean unknownExchange(final UUID exchangeId) { - UUID exchangeId = UUID.fromString(exchangeIdString); ConfiguredObjectRecord localRecord = getUpdateMap().get(exchangeId); return !((localRecord != null && localRecord.getType().equals(Exchange.class.getSimpleName())) || _exchangeRegistry.getExchange(exchangeId) != null); } - private boolean unknownQueue(final String queueIdString) + private boolean unknownQueue(final UUID queueId) { - UUID queueId = UUID.fromString(queueIdString); ConfiguredObjectRecord localRecord = getUpdateMap().get(queueId); return !((localRecord != null && localRecord.getType().equals(Queue.class.getSimpleName())) || _virtualHost.getQueue(queueId) != null); @@ -240,22 +244,24 @@ public class DefaultUpgraderProvider implements UpgraderProvider private static final String ARGUMENTS = "arguments"; @Override - public void configuredObject(UUID id, String type, Map attributes) + public void configuredObject(ConfiguredObjectRecord record) { - if(Queue.class.getSimpleName().equals(type)) + + if(Queue.class.getSimpleName().equals(record.getType())) { Map newAttributes = new LinkedHashMap(); - if(attributes.get(ARGUMENTS) instanceof Map) + if(record.getAttributes().get(ARGUMENTS) instanceof Map) { - newAttributes.putAll(QueueArgumentsConverter.convertWireArgsToModel((Map) attributes + newAttributes.putAll(QueueArgumentsConverter.convertWireArgsToModel((Map) record.getAttributes() .get(ARGUMENTS))); } - newAttributes.putAll(attributes); - attributes = newAttributes; - getUpdateMap().put(id, new ConfiguredObjectRecord(id,type,attributes)); + newAttributes.putAll(record.getAttributes()); + + record = new ConfiguredObjectRecordImpl(record.getId(), record.getType(), newAttributes, record.getParents()); + getUpdateMap().put(record.getId(), record); } - getNextUpgrader().configuredObject(id,type,attributes); + getNextUpgrader().configuredObject(record); } @Override @@ -274,16 +280,17 @@ public class DefaultUpgraderProvider implements UpgraderProvider { @Override - public void configuredObject(UUID id, String type, Map attributes) + public void configuredObject(ConfiguredObjectRecord record) { - if(Queue.class.getSimpleName().equals(type)) + + if(Queue.class.getSimpleName().equals(record.getType())) { - Map newAttributes = new LinkedHashMap(attributes); - if(attributes.get(EXCLUSIVE) instanceof Boolean) + Map newAttributes = new LinkedHashMap(record.getAttributes()); + if(record.getAttributes().get(EXCLUSIVE) instanceof Boolean) { - boolean isExclusive = (Boolean) attributes.get(EXCLUSIVE); + boolean isExclusive = (Boolean) record.getAttributes().get(EXCLUSIVE); newAttributes.put(EXCLUSIVE, isExclusive ? "CONTAINER" : "NONE"); - if(!isExclusive && attributes.containsKey("owner")) + if(!isExclusive && record.getAttributes().containsKey("owner")) { newAttributes.remove("owner"); } @@ -292,15 +299,16 @@ public class DefaultUpgraderProvider implements UpgraderProvider { newAttributes.remove("owner"); } - if(!attributes.containsKey("durable")) + if(!record.getAttributes().containsKey("durable")) { newAttributes.put("durable","true"); } - attributes = newAttributes; - getUpdateMap().put(id, new ConfiguredObjectRecord(id,type,attributes)); + + record = new ConfiguredObjectRecordImpl(record.getId(),record.getType(),newAttributes, record.getParents()); + getUpdateMap().put(record.getId(), record); } - getNextUpgrader().configuredObject(id,type,attributes); + getNextUpgrader().configuredObject(record); } @Override diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java index 2743b0ef59..4431fc786d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java @@ -28,6 +28,7 @@ import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; import org.apache.qpid.server.util.ServerScopedRuntimeException; @@ -50,11 +51,9 @@ public class ExchangeRecoverer extends AbstractDurableConfiguredObjectRecoverer< } @Override - public UnresolvedObject createUnresolvedObject(final UUID id, - final String type, - final Map attributes) + public UnresolvedObject createUnresolvedObject(final ConfiguredObjectRecord record) { - return new UnresolvedExchange(id, attributes); + return new UnresolvedExchange(record.getId(), record.getAttributes()); } private class UnresolvedExchange implements UnresolvedObject diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java index 385f4e219e..eb1bc883ca 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueFactory; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; @@ -58,11 +59,9 @@ public class QueueRecoverer extends AbstractDurableConfiguredObjectRecoverer createUnresolvedObject(final UUID id, - final String type, - final Map attributes) + public UnresolvedObject createUnresolvedObject(final ConfiguredObjectRecord record) { - return new UnresolvedQueue(id, type, attributes); + return new UnresolvedQueue(record.getId(), record.getAttributes()); } private class UnresolvedQueue implements UnresolvedObject @@ -75,7 +74,6 @@ public class QueueRecoverer extends AbstractDurableConfiguredObjectRecoverer attributes) { _attributes = attributes; diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java index 04c2f974c9..0e30096047 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.store; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; @@ -43,6 +42,8 @@ import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLogger; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.ExclusivityPolicy; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; @@ -68,6 +69,10 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest private static final String BINDING = org.apache.qpid.server.model.Binding.class.getSimpleName(); private static final String QUEUE = Queue.class.getSimpleName(); + private static final UUID ANY_UUID = UUID.randomUUID(); + private static final Map ANY_MAP = new HashMap(); + + private String _storePath; private String _storeName; private MessageStore _messageStore; @@ -117,10 +122,17 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest when(_exchange.getId()).thenReturn(_exchangeId); when(_exchange.getExchangeType()).thenReturn(mock(ExchangeType.class)); when(_exchange.getEventLogger()).thenReturn(new EventLogger()); + + ConfiguredObjectRecord exchangeRecord = mock(ConfiguredObjectRecord.class); + when(exchangeRecord.getId()).thenReturn(_exchangeId); + when(exchangeRecord.getType()).thenReturn(Exchange.class.getSimpleName()); + when(_exchange.asObjectRecord()).thenReturn(exchangeRecord); + when(_configuration.getString(eq(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY), anyString())).thenReturn( _storePath); when(_virtualHost.getAttribute(eq(VirtualHost.STORE_PATH))).thenReturn(_storePath); + _bindingArgs = new HashMap(); String argKey = AMQPFilterTypes.JMS_SELECTOR.toString(); String argValue = "some selector expression"; @@ -149,8 +161,8 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.createExchange(_configStore, exchange); reopenStore(); - verify(_recoveryHandler).configuredObject(eq(_exchangeId), eq(EXCHANGE), - eq(map( org.apache.qpid.server.model.Exchange.NAME, getName(), + verify(_recoveryHandler).configuredObject(matchesRecord(_exchangeId, EXCHANGE, + map( org.apache.qpid.server.model.Exchange.NAME, getName(), org.apache.qpid.server.model.Exchange.TYPE, getName()+"Type", org.apache.qpid.server.model.Exchange.LIFETIME_POLICY, LifetimePolicy.DELETE_ON_NO_OUTBOUND_LINKS.name()))); } @@ -183,7 +195,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.removeExchange(_configStore, exchange); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), anyString(), anyMap()); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); } public void testBindQueue() throws Exception @@ -191,42 +203,86 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false, null); BindingImpl binding = new BindingImpl(UUIDGenerator.generateRandomUUID(), ROUTING_KEY, queue, _exchange, _bindingArgs); + DurableConfigurationStoreHelper.createQueue(_configStore, queue); DurableConfigurationStoreHelper.createBinding(_configStore, binding); reopenStore(); Map map = new HashMap(); - map.put(org.apache.qpid.server.model.Binding.EXCHANGE, _exchange.getId().toString()); - map.put(org.apache.qpid.server.model.Binding.QUEUE, queue.getId().toString()); - map.put(org.apache.qpid.server.model.Binding.NAME, ROUTING_KEY); - map.put(org.apache.qpid.server.model.Binding.ARGUMENTS,_bindingArgs); + map.put(Binding.NAME, ROUTING_KEY); + map.put(Binding.ARGUMENTS,_bindingArgs); + + Map parents = new HashMap(); + + parents.put(Exchange.class.getSimpleName(), _exchange.getId()); + parents.put(Queue.class.getSimpleName(), queue.getId()); + + verify(_recoveryHandler).configuredObject(matchesRecord(binding.getId(), BINDING, map, parents)); + } + + + private ConfiguredObjectRecord matchesRecord(UUID id, + String type, + Map attributes, + final Map parents) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes, parents)); + } - verify(_recoveryHandler).configuredObject(eq(binding.getId()), eq(BINDING), - argThat(new IgnoreCreatedByMatcher(map))); + private ConfiguredObjectRecord matchesRecord(UUID id, String type, Map attributes) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes, ANY_MAP)); } - private static class IgnoreCreatedByMatcher extends ArgumentMatcher> + private static class ConfiguredObjectMatcher extends ArgumentMatcher { private final Map _matchingMap; + private final UUID _id; + private final String _name; + private final Map _parents; - private IgnoreCreatedByMatcher(final Map matchingMap) + private ConfiguredObjectMatcher(final UUID id, final String type, final Map matchingMap, Map parents) { + _id = id; + _name = type; _matchingMap = matchingMap; + _parents = parents; } @Override public boolean matches(final Object argument) { - if(argument instanceof Map) + if(argument instanceof ConfiguredObjectRecord) { - Map arg = new HashMap((Map) argument); + ConfiguredObjectRecord binding = (ConfiguredObjectRecord) argument; + + Map arg = new HashMap(binding.getAttributes()); arg.remove("createdBy"); arg.remove("createdTime"); - return arg.equals(_matchingMap); - + return (_id == ANY_UUID || _id.equals(binding.getId())) + && _name.equals(binding.getType()) + && (_matchingMap == ANY_MAP || arg.equals(_matchingMap)) + && (_parents == ANY_MAP || matchesParents(binding)); } return false; } + + private boolean matchesParents(ConfiguredObjectRecord binding) + { + Map bindingParents = binding.getParents(); + if(bindingParents.size() != _parents.size()) + { + return false; + } + for(Map.Entry entry : _parents.entrySet()) + { + if(!bindingParents.get(entry.getKey()).getId().equals(entry.getValue())) + { + return false; + } + } + return true; + } } public void testUnbindQueue() throws Exception @@ -239,9 +295,8 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.removeBinding(_configStore, binding); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), - eq(BINDING), - anyMap()); + verify(_recoveryHandler, never()).configuredObject(matchesRecord(ANY_UUID, BINDING, + ANY_MAP)); } public void testCreateQueueAMQQueue() throws Exception @@ -254,7 +309,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.NAME, getName()); queueAttributes.put(Queue.OWNER, getName()+"Owner"); queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testCreateQueueAMQQueueFieldTable() throws Exception @@ -276,7 +331,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); queueAttributes.putAll(attributes); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testCreateQueueAMQQueueWithAlternateExchange() throws Exception @@ -294,7 +349,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); queueAttributes.put(Queue.ALTERNATE_EXCHANGE, alternateExchange.getId().toString()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } private ExchangeImpl createTestAlternateExchange() @@ -327,7 +382,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.NAME, getName()); queueAttributes.putAll(attributes); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } @@ -354,7 +409,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.putAll(attributes); queueAttributes.put(Queue.ALTERNATE_EXCHANGE, alternateExchange.getId().toString()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testRemoveQueue() throws Exception @@ -369,9 +424,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest // remove queue DurableConfigurationStoreHelper.removeQueue(_configStore,queue); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), - eq(org.apache.qpid.server.model.Queue.class.getName()), - anyMap()); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); } private AMQQueue createTestQueue(String queueName, @@ -424,6 +477,12 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest }); when(queue.getActualAttributes()).thenReturn(attributes); + + ConfiguredObjectRecord objectRecord = mock(ConfiguredObjectRecord.class); + when(objectRecord.getId()).thenReturn(_queueId); + when(objectRecord.getType()).thenReturn(Queue.class.getSimpleName()); + when(objectRecord.getAttributes()).thenReturn(attributes); + when(queue.asObjectRecord()).thenReturn(objectRecord); return queue; } @@ -440,6 +499,13 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest when(exchange.getTypeName()).thenReturn(getName() + "Type"); when(exchange.isAutoDelete()).thenReturn(true); when(exchange.getId()).thenReturn(_exchangeId); + ConfiguredObjectRecord exchangeRecord = mock(ConfiguredObjectRecord.class); + when(exchangeRecord.getId()).thenReturn(_exchangeId); + when(exchangeRecord.getType()).thenReturn(Exchange.class.getSimpleName()); + Map actualAttributesExceptId = new HashMap(actualAttributes); + actualAttributesExceptId.remove("id"); + when(exchangeRecord.getAttributes()).thenReturn(actualAttributesExceptId); + when(exchange.asObjectRecord()).thenReturn(exchangeRecord); return exchange; } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java index 224a22687f..4304b59d10 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java @@ -31,11 +31,13 @@ import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.util.ServerScopedRuntimeException; import org.apache.qpid.test.utils.QpidTestCase; +import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -49,6 +51,10 @@ public class JsonFileConfigStoreTest extends QpidTestCase private VirtualHost _virtualHost; private JsonFileConfigStore _store; + + private static final UUID ANY_UUID = UUID.randomUUID(); + private static final Map ANY_MAP = new HashMap(); + @Override public void setUp() throws Exception { @@ -109,7 +115,7 @@ public class JsonFileConfigStoreTest extends QpidTestCase _store.configureConfigStore(_virtualHost, _recoveryHandler); InOrder inorder = inOrder(_recoveryHandler); inorder.verify(_recoveryHandler).beginConfigurationRecovery(eq(_store), eq(0)); - inorder.verify(_recoveryHandler,never()).configuredObject(any(UUID.class),anyString(),anyMap()); + inorder.verify(_recoveryHandler,never()).configuredObject(any(ConfiguredObjectRecord.class)); inorder.verify(_recoveryHandler).completeConfigurationRecovery(); _store.close(); } @@ -141,11 +147,11 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); final Map queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + _store.create(new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); _store.close(); _store.configureConfigStore(_virtualHost, _recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq(queueType), eq(queueAttr)); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, queueType, queueAttr)); _store.close(); } @@ -156,17 +162,17 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); Map queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + _store.create(new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); queueAttr = new HashMap(queueAttr); queueAttr.put("owner", "theowner"); - _store.update(queueId, queueType, queueAttr); + _store.update(false, new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); _store.close(); _store.configureConfigStore(_virtualHost, _recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq(queueType), eq(queueAttr)); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, queueType, queueAttr)); _store.close(); } @@ -178,15 +184,16 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); Map queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + final ConfiguredObjectRecordImpl record = new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr); + _store.create(record); - _store.remove(queueId, queueType); + _store.remove(record); _store.close(); _store.configureConfigStore(_virtualHost, _recoveryHandler); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), anyString(), anyMap()); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); _store.close(); } @@ -195,7 +202,7 @@ public class JsonFileConfigStoreTest extends QpidTestCase _store.configureConfigStore(_virtualHost, _recoveryHandler); try { - _store.create(UUID.randomUUID(), "wibble", Collections.emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(UUID.randomUUID(), "wibble", Collections.emptyMap())); fail("Should not be able to create instance of type wibble"); } catch (StoreException e) @@ -208,10 +215,10 @@ public class JsonFileConfigStoreTest extends QpidTestCase { _store.configureConfigStore(_virtualHost, _recoveryHandler); final UUID id = UUID.randomUUID(); - _store.create(id, "Queue", Collections.emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Queue", Collections.emptyMap())); try { - _store.create(id, "Exchange", Collections.emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Exchange", Collections.emptyMap())); fail("Should not be able to create two objects with same id"); } catch (StoreException e) @@ -225,13 +232,13 @@ public class JsonFileConfigStoreTest extends QpidTestCase { _store.configureConfigStore(_virtualHost, _recoveryHandler); final UUID id = UUID.randomUUID(); - _store.create(id, "Queue", Collections.emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Queue", Collections.emptyMap())); _store.close(); _store.configureConfigStore(_virtualHost, _recoveryHandler); try { - _store.update(id, "Exchange", Collections.emptyMap()); + _store.update(false, new ConfiguredObjectRecordImpl(id, "Exchange", Collections.emptyMap())); fail("Should not be able to update object to different type"); } catch (StoreException e) @@ -270,31 +277,75 @@ public class JsonFileConfigStoreTest extends QpidTestCase final Map EMPTY_ATTR = Collections.emptyMap(); final UUID exchangeId = new UUID(0, 2); - final Map bindingAttributes = new HashMap(); - bindingAttributes.put(Binding.EXCHANGE, exchangeId); - bindingAttributes.put(Binding.QUEUE, queueId); - final Map binding2Attributes = new HashMap(); - binding2Attributes.put(Binding.EXCHANGE, exchangeId); - binding2Attributes.put(Binding.QUEUE, queue2Id); final UUID bindingId = new UUID(0, 3); final UUID binding2Id = new UUID(1, 3); - _store.create(queueId, "Queue", EMPTY_ATTR); - _store.create(queue2Id, "Queue", EMPTY_ATTR); - _store.create(exchangeId, "Exchange", EMPTY_ATTR); - _store.update(true, - new ConfiguredObjectRecord(bindingId, "Binding", bindingAttributes), - new ConfiguredObjectRecord(binding2Id, "Binding", binding2Attributes)); + final ConfiguredObjectRecordImpl queueRecord = new ConfiguredObjectRecordImpl(queueId, "Queue", EMPTY_ATTR); + _store.create(queueRecord); + final ConfiguredObjectRecordImpl queue2Record = new ConfiguredObjectRecordImpl(queue2Id, "Queue", EMPTY_ATTR); + _store.create(queue2Record); + final ConfiguredObjectRecordImpl exchangeRecord = new ConfiguredObjectRecordImpl(exchangeId, "Exchange", EMPTY_ATTR); + _store.create(exchangeRecord); + Map bindingParents = new HashMap(); + bindingParents.put("Exchange", exchangeRecord); + bindingParents.put("Queue", queueRecord); + final ConfiguredObjectRecordImpl bindingRecord = + new ConfiguredObjectRecordImpl(bindingId, "Binding", EMPTY_ATTR, bindingParents); + + + Map binding2Parents = new HashMap(); + binding2Parents.put("Exchange", exchangeRecord); + binding2Parents.put("Queue", queue2Record); + final ConfiguredObjectRecordImpl binding2Record = + new ConfiguredObjectRecordImpl(binding2Id, "Binding", EMPTY_ATTR, binding2Parents); + _store.update(true, bindingRecord, binding2Record); _store.close(); _store.configureConfigStore(_virtualHost, _recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq("Queue"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(queue2Id), eq("Queue"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(exchangeId), eq("Exchange"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(bindingId),eq("Binding"), eq(bindingAttributes)); - verify(_recoveryHandler).configuredObject(eq(binding2Id),eq("Binding"), eq(binding2Attributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, "Queue", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(queue2Id, "Queue", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(exchangeId, "Exchange", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(bindingId, "Binding", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(binding2Id, "Binding", EMPTY_ATTR)); _store.close(); } + private ConfiguredObjectRecord matchesRecord(UUID id, String type, Map attributes) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes)); + } + + private static class ConfiguredObjectMatcher extends ArgumentMatcher + { + private final Map _matchingMap; + private final UUID _id; + private final String _name; + + private ConfiguredObjectMatcher(final UUID id, final String type, final Map matchingMap) + { + _id = id; + _name = type; + _matchingMap = matchingMap; + } + + @Override + public boolean matches(final Object argument) + { + if(argument instanceof ConfiguredObjectRecord) + { + ConfiguredObjectRecord binding = (ConfiguredObjectRecord) argument; + + Map arg = new HashMap(binding.getAttributes()); + arg.remove("createdBy"); + arg.remove("createdTime"); + return (_id == ANY_UUID || _id.equals(binding.getId())) + && _name.equals(binding.getType()) + && (_matchingMap == ANY_MAP || arg.equals(_matchingMap)); + + } + return false; + } + } + } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java index 9f1f6f48c0..b784405881 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java @@ -30,6 +30,7 @@ import java.util.UUID; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.exchange.DirectExchange; @@ -214,17 +215,16 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(DIRECT_EXCHANGE_ID, QUEUE_ID))); final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "Binding", - createBinding("key", DIRECT_EXCHANGE_ID, QUEUE_ID)) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", + createBinding("key")) }; verifyCorrectUpdates(expected); @@ -239,32 +239,30 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", "wibble", "not-a-selector", - "moo")); + "moo"), + createBindingParents(DIRECT_EXCHANGE_ID, QUEUE_ID))); final UUID customExchangeId = new UUID(3,0); - _durableConfigurationRecoverer.configuredObject(new UUID(2, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(2, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - customExchangeId, - QUEUE_ID, "x-filter-jms-selector", "wibble", "not-a-selector", - "moo")); + "moo"), + createBindingParents(customExchangeId,QUEUE_ID))); - _durableConfigurationRecoverer.configuredObject(customExchangeId, + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(customExchangeId, "org.apache.qpid.server.model.Exchange", - createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE)); + createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE))); final ExchangeImpl customExchange = mock(ExchangeImpl.class); @@ -291,10 +289,10 @@ public class DurableConfigurationRecovererTest extends QpidTestCase final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "org.apache.qpid.server.model.Binding", - createBinding("key", DIRECT_EXCHANGE_ID, QUEUE_ID, "not-a-selector", "moo")), - new ConfiguredObjectRecord(new UUID(2, 0), "org.apache.qpid.server.model.Binding", - createBinding("key", customExchangeId, QUEUE_ID, "not-a-selector", "moo")) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", + createBinding("key", "not-a-selector", "moo")), + new ConfiguredObjectRecordImpl(new UUID(2, 0), "org.apache.qpid.server.model.Binding", + createBinding("key", "not-a-selector", "moo")) }; verifyCorrectUpdates(expected); @@ -308,17 +306,16 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - TOPIC_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(TOPIC_EXCHANGE_ID,QUEUE_ID))); final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "Binding", - createBinding("key", TOPIC_EXCHANGE_ID, QUEUE_ID, "x-filter-jms-selector", "wibble")) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", + createBinding("key", "x-filter-jms-selector", "wibble")) }; verifyCorrectUpdates(expected); @@ -331,16 +328,15 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(DIRECT_EXCHANGE_ID,QUEUE_ID))); doThrow(new RuntimeException("Update Should not be called")) - .when(_store).update(anyBoolean(), any(ConfiguredObjectRecord[].class)); + .when(_store).update(anyBoolean(), any(ConfiguredObjectRecordImpl[].class)); _durableConfigurationRecoverer.completeConfigurationRecovery(); } @@ -350,13 +346,13 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", createBinding("key", - new UUID(3,0), - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(new UUID(3,0), + QUEUE_ID))); try { @@ -378,8 +374,8 @@ public class DurableConfigurationRecovererTest extends QpidTestCase try { final Map emptyArguments = Collections.emptyMap(); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), - "Wibble", emptyArguments); + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), + "Wibble", emptyArguments)); _durableConfigurationRecoverer.completeConfigurationRecovery(); fail("Expected resolution to fail due to unknown object type"); } @@ -425,11 +421,11 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(queueId, Queue.class.getSimpleName(), - createQueue("testQueue", exchangeId)); - _durableConfigurationRecoverer.configuredObject(exchangeId, + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(queueId, Queue.class.getSimpleName(), + createQueue("testQueue", exchangeId))); + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(exchangeId, org.apache.qpid.server.model.Exchange.class.getSimpleName(), - createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE)); + createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE))); _durableConfigurationRecoverer.completeConfigurationRecovery(); @@ -450,16 +446,14 @@ public class DurableConfigurationRecovererTest extends QpidTestCase return null; } - }).when(_store).update(anyBoolean(), any(ConfiguredObjectRecord[].class)); + }).when(_store).update(anyBoolean(), any(ConfiguredObjectRecordImpl[].class)); } - private Map createBinding(String bindingKey, UUID exchangeId, UUID queueId, String... args) + private Map createBinding(String bindingKey, String... args) { Map binding = new LinkedHashMap(); binding.put("name", bindingKey); - binding.put(Binding.EXCHANGE, exchangeId.toString()); - binding.put(Binding.QUEUE, queueId.toString()); Map argumentMap = new LinkedHashMap(); if(args != null && args.length != 0) { @@ -481,6 +475,15 @@ public class DurableConfigurationRecovererTest extends QpidTestCase return binding; } + private Map createBindingParents(UUID exchangeId, UUID queueId) + { + Map parents = new HashMap(); + parents.put("Exchange", new ConfiguredObjectRecordImpl(exchangeId,"Exchange",Collections.emptyMap())); + parents.put("Queue", new ConfiguredObjectRecordImpl(queueId,"Queue",Collections.emptyMap())); + + return parents; + } + private Map createExchange(String name, ExchangeType type) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java index 1d7c2ffa46..0e80d72d9b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -190,6 +190,12 @@ public class MessageStoreTest extends QpidTestCase try { _virtualHost = BrokerTestHelper.createVirtualHost(new VirtualHostConfiguration(getClass().getName(), _config, _broker),new VirtualHostRegistry(_broker.getEventLogger()),getVirtualHostModel()); + when(_virtualHostModel.getId()).thenReturn(_virtualHost.getId()); + + ConfiguredObjectRecord objectRecord = mock(ConfiguredObjectRecord.class); + when(objectRecord.getId()).thenReturn(_virtualHost.getId()); + when(objectRecord.getType()).thenReturn(org.apache.qpid.server.model.VirtualHost.class.getSimpleName()); + when(_virtualHostModel.asObjectRecord()).thenReturn(objectRecord); } catch (Exception e) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java index 9407ef2d6c..2def24205c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java @@ -184,40 +184,24 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore return _realStore.addMessage(metaData); } - @Override - public void create(UUID id, String type, Map attributes) throws StoreException + public void create(ConfiguredObjectRecord record) throws StoreException { doPreDelay("create"); - _durableConfigurationStore.create(id, type, attributes); + _durableConfigurationStore.create(record); doPostDelay("create"); } - @Override - public void remove(UUID id, String type) throws StoreException - { - doPreDelay("remove"); - _durableConfigurationStore.remove(id, type); - doPostDelay("remove"); - } @Override - public UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException + public UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { doPreDelay("remove"); - UUID[] removed = _durableConfigurationStore.removeConfiguredObjects(objects); + UUID[] removed = _durableConfigurationStore.remove(objects); doPostDelay("remove"); return removed; } - @Override - public void update(UUID id, String type, Map attributes) throws StoreException - { - doPreDelay("update"); - _durableConfigurationStore.update(id, type, attributes); - doPostDelay("update"); - } - @Override public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException { -- cgit v1.2.1 From 4eb25f6f4ed6fe03bd7ec86dfbb3f4a4e372ed97 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Mon, 24 Mar 2014 15:35:10 +0000 Subject: QPID-5639 : Remove Unicode from source file git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1580885 13f79535-47bb-0310-9956-ffa450edef68 --- .../security/auth/manager/ScramSHA1AuthenticationManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/java') diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java index 78b4b22ff3..033e0afde8 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java @@ -191,7 +191,7 @@ public class ScramSHA1AuthenticationManagerTest extends QpidTestCase { try { - _authManager.createUser(getTestName()+"£", "password", Collections.emptyMap()); + _authManager.createUser(getTestName()+Character.toString((char)0xa3), "password", Collections.emptyMap()); fail("Expected exception when attempting to create a user with a non ascii name"); } catch(IllegalArgumentException e) -- cgit v1.2.1