summaryrefslogtreecommitdiff
path: root/java/broker-plugins
diff options
context:
space:
mode:
authorKim van der Riet <kpvdr@apache.org>2012-08-03 12:13:32 +0000
committerKim van der Riet <kpvdr@apache.org>2012-08-03 12:13:32 +0000
commitd43d1912b376322e27fdcda551a73f9ff5487972 (patch)
treece493e10baa95f44be8beb5778ce51783463196d /java/broker-plugins
parent04877fec0c6346edec67072d7f2d247740cf2af5 (diff)
downloadqpid-python-d43d1912b376322e27fdcda551a73f9ff5487972.tar.gz
QPID-3858: Updated branch - merged from trunk r.1368650
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1368910 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/broker-plugins')
-rw-r--r--java/broker-plugins/access-control/MANIFEST.MF2
-rw-r--r--java/broker-plugins/access-control/build.xml7
-rw-r--r--java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java7
-rw-r--r--java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java90
-rw-r--r--java/broker-plugins/experimental/shutdown/MANIFEST.MF16
-rw-r--r--java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java57
-rwxr-xr-xjava/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd25
-rw-r--r--java/broker-plugins/extras/MANIFEST.MF21
-rw-r--r--java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java214
-rw-r--r--java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java59
-rw-r--r--java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java262
-rw-r--r--java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java59
-rw-r--r--java/broker-plugins/extras/src/test/java/org/apache/qpid/server/plugins/ExtrasTest.java75
-rw-r--r--java/broker-plugins/firewall/MANIFEST.MF2
-rw-r--r--java/broker-plugins/firewall/build.xml7
-rw-r--r--java/broker-plugins/management-http/MANIFEST.MF70
-rw-r--r--java/broker-plugins/management-http/build.xml57
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java248
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java73
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java77
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java79
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java109
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java208
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java118
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java208
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java53
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java85
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java74
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java176
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java502
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java586
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java256
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java103
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/addBinding.html42
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/addExchange.html54
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/addQueue.html182
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/addUser.html42
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/setPassword.html42
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html (renamed from java/broker-plugins/experimental/shutdown/build.xml)21
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/css/common.css92
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/footer.html (renamed from java/broker-plugins/extras/build.xml)25
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.pngbin0 -> 6600 bytes
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js213
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js110
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/footer.js30
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js99
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/properties.js26
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/updater.js45
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js120
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js122
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js205
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Connection.js213
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js291
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Queue.js485
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js378
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addBinding.js223
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addExchange.js146
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addQueue.js191
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js327
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js104
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/moveCopyMessages.js137
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js142
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js313
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/management.html92
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/moveCopyMessages.html36
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html25
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showBroker.html25
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showConnection.html47
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showExchange.html50
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showMessage.html73
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showQueue.html103
-rw-r--r--java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html87
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/Asserts.java249
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java53
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java109
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java86
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java118
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java213
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java67
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java42
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java354
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java41
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java245
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java225
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java22
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java115
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java92
-rw-r--r--java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java434
-rw-r--r--java/broker-plugins/management-jmx/MANIFEST.MF66
-rw-r--r--java/broker-plugins/management-jmx/build.xml47
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java84
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java189
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java136
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java76
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java499
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java193
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java400
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java382
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java52
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java57
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java48
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java200
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java56
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java182
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java313
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java324
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java57
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java668
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java89
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java (renamed from java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java)27
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java (renamed from java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java)11
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java179
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java211
-rw-r--r--java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java238
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java (renamed from java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/Activator.java)37
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java237
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBeanTest.java234
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java245
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java40
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java439
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBeanTest.java94
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java157
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java174
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java124
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java283
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java149
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java480
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java317
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java696
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java204
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java251
-rw-r--r--java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java39
132 files changed, 19488 insertions, 904 deletions
diff --git a/java/broker-plugins/access-control/MANIFEST.MF b/java/broker-plugins/access-control/MANIFEST.MF
index 78072850e4..a8fb99995e 100644
--- a/java/broker-plugins/access-control/MANIFEST.MF
+++ b/java/broker-plugins/access-control/MANIFEST.MF
@@ -13,12 +13,10 @@ Bundle-ActivationPolicy: lazy
Import-Package: org.apache.qpid,
org.apache.qpid.exchange,
org.apache.qpid.framing,
- org.apache.qpid.junit.extensions.util,
org.apache.qpid.protocol,
org.apache.qpid.server.configuration,
org.apache.qpid.server.configuration.plugins,
org.apache.qpid.server.exchange,
- org.apache.qpid.server.management,
org.apache.qpid.server.logging,
org.apache.qpid.server.logging.actors,
org.apache.qpid.server.logging.subjects,
diff --git a/java/broker-plugins/access-control/build.xml b/java/broker-plugins/access-control/build.xml
index 89f8240fd5..df3346788c 100644
--- a/java/broker-plugins/access-control/build.xml
+++ b/java/broker-plugins/access-control/build.xml
@@ -17,15 +17,20 @@
- under the License.
-->
<project name="Qpid Broker-Plugins Access Control" default="build">
- <property name="module.depends" value="common broker broker-plugins" />
+ <property name="module.depends" value="common broker" />
<property name="module.test.depends" value="test common/test broker/test management/common systests" />
<property name="module.manifest" value="MANIFEST.MF" />
<property name="module.plugin" value="true" />
+ <property name="module.genpom" value="true"/>
+ <property name="module.genpom.args" value="-Sqpid-common=provided -Sqpid-broker=provided"/>
+
+ <property name="broker-plugins-access-control.libs" value=""/>
<import file="../../module.xml" />
<target name="bundle" depends="bundle-tasks"/>
<target name="precompile" depends="gen_logging"/>
+
</project>
diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
index 25f903e860..c2282694fb 100644
--- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
+++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
@@ -38,7 +38,7 @@ import org.apache.qpid.server.security.access.config.RuleSet;
/**
* These tests check that the ACL file parsing works correctly.
- *
+ *
* For each message that can be returned in a {@link ConfigurationException}, an ACL file is created that should trigger this
* particular message.
*/
@@ -48,7 +48,7 @@ public class PlainConfigurationTest extends TestCase
{
File acl = File.createTempFile(getClass().getName() + getName(), "acl");
acl.deleteOnExit();
-
+
// Write ACL file
PrintWriter aclWriter = new PrintWriter(new FileWriter(acl));
for (String line : aclData)
@@ -70,14 +70,13 @@ public class PlainConfigurationTest extends TestCase
// Load ruleset
ConfigurationFile configFile = new PlainConfiguration(new File("doesnotexist"));
configFile.load();
-
+
fail("fail");
}
catch (ConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.CONFIG_NOT_FOUND_MSG, "doesnotexist"), ce.getMessage());
assertTrue(ce.getCause() instanceof FileNotFoundException);
- assertEquals("doesnotexist (No such file or directory)", ce.getCause().getMessage());
}
}
diff --git a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
index 4d46a32f45..f7cc60543d 100644
--- a/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
+++ b/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
@@ -40,12 +40,12 @@ import org.apache.qpid.test.utils.QpidTestCase;
/**
* This test checks that the {@link RuleSet} object which forms the core of the access control plugin performs correctly.
- *
+ *
* The ruleset is configured directly rather than using an external file by adding rules individually, calling the
* {@link RuleSet#grant(Integer, String, Permission, Operation, ObjectType, ObjectProperties)} method. Then, the
* access control mechanism is validated by checking whether operations would be authorised by calling the
* {@link RuleSet#check(Principal, Operation, ObjectType, ObjectProperties)} method.
- *
+ *
* It ensure that permissions can be granted correctly on users directly, ACL groups (that is those
* groups declared directly in the ACL itself), and External groups (that is a group from an External
* Authentication Provider, such as an LDAP).
@@ -82,11 +82,11 @@ public class RuleSetTest extends QpidTestCase
{
assertDenyGrantAllow(subject, operation, objectType, ObjectProperties.EMPTY);
}
-
+
public void assertDenyGrantAllow(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties)
{
- final Principal identity = UsernamePrincipal.getUsernamePrincipalFromSubject(subject);
-
+ 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);
assertEquals(1, _ruleSet.getRuleCount());
@@ -99,7 +99,7 @@ public class RuleSetTest extends QpidTestCase
assertEquals(_ruleSet.getRuleCount(), 0);
assertEquals(_ruleSet.getDefault(), _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
-
+
public void testVirtualHostAccess() throws Exception
{
assertDenyGrantAllow(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST);
@@ -114,7 +114,7 @@ public class RuleSetTest extends QpidTestCase
{
ObjectProperties properties = new ObjectProperties(_queueName);
properties.put(ObjectProperties.Property.ROUTING_KEY, (String) null);
-
+
assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.QUEUE, properties);
}
@@ -122,7 +122,7 @@ public class RuleSetTest extends QpidTestCase
{
ObjectProperties properties = new ObjectProperties(_exchangeName);
properties.put(ObjectProperties.Property.TYPE, _exchangeType.asString());
-
+
assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.EXCHANGE, properties);
}
@@ -144,15 +144,15 @@ public class RuleSetTest extends QpidTestCase
{
ObjectProperties temporary = new ObjectProperties();
temporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
ObjectProperties normal = new ObjectProperties();
normal.put(ObjectProperties.Property.AUTO_DELETE, Boolean.FALSE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
_ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CONSUME, ObjectType.QUEUE, temporary);
assertEquals(1, _ruleSet.getRuleCount());
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
-
+
// defer to global if exists, otherwise default answer - this is handled by the security manager
assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, normal));
}
@@ -164,17 +164,17 @@ public class RuleSetTest extends QpidTestCase
{
ObjectProperties temporary = new ObjectProperties(_queueName);
temporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
ObjectProperties normal = new ObjectProperties(_queueName);
normal.put(ObjectProperties.Property.AUTO_DELETE, Boolean.FALSE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
// should not matter if the temporary permission is processed first or last
_ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.CONSUME, ObjectType.QUEUE, normal);
_ruleSet.grant(2, TEST_USER, Permission.ALLOW, Operation.CONSUME, ObjectType.QUEUE, temporary);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, normal));
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
}
@@ -186,17 +186,17 @@ public class RuleSetTest extends QpidTestCase
{
ObjectProperties temporary = new ObjectProperties(_queueName);
temporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
ObjectProperties normal = new ObjectProperties(_queueName);
normal.put(ObjectProperties.Property.AUTO_DELETE, Boolean.FALSE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
// should not matter if the temporary permission is processed first or last
_ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.CONSUME, ObjectType.QUEUE, temporary);
_ruleSet.grant(2, TEST_USER, Permission.ALLOW, Operation.CONSUME, ObjectType.QUEUE, normal);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, normal));
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CONSUME, ObjectType.QUEUE, temporary));
}
@@ -204,7 +204,7 @@ public class RuleSetTest extends QpidTestCase
/*
* Test different rules for temporary queues.
*/
-
+
/**
* The more generic rule first is used, so both requests are allowed.
*/
@@ -213,18 +213,18 @@ public class RuleSetTest extends QpidTestCase
ObjectProperties named = new ObjectProperties(_queueName);
ObjectProperties namedTemporary = new ObjectProperties(_queueName);
namedTemporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
_ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, named);
_ruleSet.grant(2, TEST_USER, Permission.DENY, Operation.CREATE, ObjectType.QUEUE, namedTemporary);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
}
-
+
/**
* The more specific rule is first, so those requests are denied.
*/
@@ -233,18 +233,18 @@ public class RuleSetTest extends QpidTestCase
ObjectProperties named = new ObjectProperties(_queueName);
ObjectProperties namedTemporary = new ObjectProperties(_queueName);
namedTemporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
_ruleSet.grant(1, TEST_USER, Permission.DENY, Operation.CREATE, ObjectType.QUEUE, namedTemporary);
_ruleSet.grant(2, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, named);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
}
-
+
/**
* The more specific rules are first, so those requests are denied.
*/
@@ -255,7 +255,7 @@ public class RuleSetTest extends QpidTestCase
namedTemporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
ObjectProperties namedDurable = new ObjectProperties(_queueName);
namedDurable.put(ObjectProperties.Property.DURABLE, Boolean.TRUE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedDurable));
@@ -264,48 +264,48 @@ public class RuleSetTest extends QpidTestCase
_ruleSet.grant(2, TEST_USER, Permission.DENY, Operation.CREATE, ObjectType.QUEUE, namedDurable);
_ruleSet.grant(3, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, named);
assertEquals(3, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedDurable));
}
-
+
public void testNamedTemporaryQueueAllowed()
{
ObjectProperties named = new ObjectProperties(_queueName);
ObjectProperties namedTemporary = new ObjectProperties(_queueName);
namedTemporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
_ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, namedTemporary);
_ruleSet.grant(2, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, named);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
}
-
+
public void testNamedTemporaryQueueDeniedAllowed()
{
ObjectProperties named = new ObjectProperties(_queueName);
ObjectProperties namedTemporary = new ObjectProperties(_queueName);
namedTemporary.put(ObjectProperties.Property.AUTO_DELETE, Boolean.TRUE);
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
_ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, namedTemporary);
_ruleSet.grant(2, TEST_USER, Permission.DENY, Operation.CREATE, ObjectType.QUEUE, named);
assertEquals(2, _ruleSet.getRuleCount());
-
+
assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, named));
assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, namedTemporary));
}
- /**
- * Tests support for the {@link Rule#ALL} keyword.
+ /**
+ * Tests support for the {@link Rule#ALL} keyword.
*/
public void testAllowToAll()
{
@@ -316,13 +316,13 @@ public class RuleSetTest extends QpidTestCase
assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
- /**
+ /**
* Tests support for ACL groups (i.e. inline groups declared in the ACL file itself).
*/
public void testAclGroupsSupported()
{
- assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera", "userb"})));
-
+ assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera", "userb"})));
+
_ruleSet.grant(1, "aclgroup", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(1, _ruleSet.getRuleCount());
@@ -331,14 +331,14 @@ public class RuleSetTest extends QpidTestCase
assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("userc"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
- /**
+ /**
* Tests support for nested ACL groups.
*/
public void testNestedAclGroupsSupported()
{
assertTrue(_ruleSet.addGroup("aclgroup1", Arrays.asList(new String[] {"userb"})));
- assertTrue(_ruleSet.addGroup("aclgroup2", Arrays.asList(new String[] {"usera", "aclgroup1"})));
-
+ assertTrue(_ruleSet.addGroup("aclgroup2", Arrays.asList(new String[] {"usera", "aclgroup1"})));
+
_ruleSet.grant(1, "aclgroup2", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(1, _ruleSet.getRuleCount());
@@ -346,7 +346,7 @@ public class RuleSetTest extends QpidTestCase
assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
- /**
+ /**
* Tests support for nested External groups (i.e. those groups coming from an external source such as an LDAP).
*/
public void testExternalGroupsSupported()
@@ -358,7 +358,7 @@ public class RuleSetTest extends QpidTestCase
assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", "extgroup1"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", "extgroup2"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
-
+
/**
* Rule order in the ACL determines the outcome of the check. This test ensures that a user who is
* granted explicit permission on an object, is granted that access even although late a group
@@ -367,7 +367,7 @@ public class RuleSetTest extends QpidTestCase
public void testAllowDeterminedByRuleOrder()
{
assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"})));
-
+
_ruleSet.grant(1, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
_ruleSet.grant(2, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(2, _ruleSet.getRuleCount());
@@ -382,10 +382,10 @@ public class RuleSetTest extends QpidTestCase
public void testDenyDeterminedByRuleOrder()
{
assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"})));
-
+
_ruleSet.grant(1, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
_ruleSet.grant(2, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
-
+
assertEquals(2, _ruleSet.getRuleCount());
assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
diff --git a/java/broker-plugins/experimental/shutdown/MANIFEST.MF b/java/broker-plugins/experimental/shutdown/MANIFEST.MF
deleted file mode 100644
index 0bd0a835e4..0000000000
--- a/java/broker-plugins/experimental/shutdown/MANIFEST.MF
+++ /dev/null
@@ -1,16 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: Experimental Shutdown
-Bundle-Description: Experimental Qpid Broker Shutdown Plugin
-Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
-Bundle-DocURL: http://qpid.apache.org/
-Bundle-SymbolicName: broker-plugins-experimental-shutdown;singleton:=true
-Bundle-Version: 1.0.0
-Bundle-Activator: org.apache.qpid.shutdown.Activator
-Import-Package: javax.management;resolution:=optional,
- org.apache.log4j,
- org.osgi.framework,
- org.apache.qpid.server.management
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Bundle-ActivationPolicy: lazy
-
diff --git a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java b/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java
deleted file mode 100644
index 2b7fa33784..0000000000
--- a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.shutdown;
-
-
-import org.apache.log4j.Logger;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-public class Activator implements BundleActivator
-{
- private static final Logger _logger = Logger.getLogger(Activator.class);
-
- private Shutdown _shutdown = null;
-
- /** @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */
- public void start(BundleContext ctx) throws Exception {
- _shutdown = new Shutdown();
- if (ctx != null)
- {
- ctx.registerService(ShutdownMBean.class.getName(), _shutdown, null);
- }
-
- _shutdown.register();
-
- _logger.info("Shutdown plugin MBean registered");
- }
-
- /** @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */
- public void stop(BundleContext ctx) throws Exception
- {
- if (_shutdown != null)
- {
- _shutdown.unregister();
- _shutdown = null;
- }
-
- _logger.info("Shutdown plugin MBean unregistered");
- }
-}
diff --git a/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd b/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd
deleted file mode 100755
index 60af4b89e8..0000000000
--- a/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-ver: 0.17.0
-
-Bundle-SymbolicName: qpid-shutdown-plugin
-Bundle-Version: ${ver}
-Export-Package: *;version=${ver}
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/java/broker-plugins/extras/MANIFEST.MF b/java/broker-plugins/extras/MANIFEST.MF
deleted file mode 100644
index f4ef6e8178..0000000000
--- a/java/broker-plugins/extras/MANIFEST.MF
+++ /dev/null
@@ -1,21 +0,0 @@
-Bundle-ManifestVersion: 2
-Bundle-Name: Qpid Broker-Plugins Extras
-Bundle-SymbolicName: broker-plugins-extras
-Bundle-Description: Extra exchange types plugin for Qpid.
-Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
-Bundle-DocURL: http://www.apache.org/
-Bundle-Activator: org.apache.qpid.extras.Activator
-Private-Package: org.apache.qpid.extras,
- org.apache.qpid.extras.exchanges.diagnostic,
- org.apache.qpid.extras.exchanges.example
-Import-Package: org.apache.qpid,
- org.apache.qpid.framing,
- org.apache.qpid.junit.extensions.util,
- org.apache.qpid.protocol,
- org.apache.qpid.server.exchange,
- org.apache.qpid.server.management,
- org.apache.qpid.server.queue,
- org.apache.qpid.server.virtualhost,
- javax.management;version=1.0.0,
- javax.management.openmbean;version=1.0.0,
- org.osgi.framework;version=1.3
diff --git a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java b/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
deleted file mode 100644
index af56b50437..0000000000
--- a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.extras.exchanges.diagnostic;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor;
-import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
-import org.apache.qpid.server.binding.Binding;
-import org.apache.qpid.server.exchange.AbstractExchange;
-import org.apache.qpid.server.exchange.AbstractExchangeMBean;
-import org.apache.qpid.server.exchange.ExchangeType;
-import org.apache.qpid.server.message.InboundMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import javax.management.JMException;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.TabularData;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * This is a special diagnostic exchange type which doesn't actually do anything
- * with messages. When it receives a message, it writes information about the
- * current memory usage to the "memory" property of the message and places it on the
- * diagnosticqueue for retrieval
- */
-public class DiagnosticExchange extends AbstractExchange
-{
- private static final Logger _logger = Logger.getLogger(DiagnosticExchange.class);
-
- public static final AMQShortString DIAGNOSTIC_EXCHANGE_CLASS = new AMQShortString("x-diagnostic");
- public static final AMQShortString DIAGNOSTIC_EXCHANGE_NAME = new AMQShortString("diagnostic");
-
- /**
- * MBean class implementing the management interfaces.
- */
- @MBeanDescription("Management Bean for Diagnostic Exchange")
- private final class DiagnosticExchangeMBean extends AbstractExchangeMBean<DiagnosticExchange>
- {
- /**
- * Usual constructor.
- *
- * @throws JMException
- */
- @MBeanConstructor("Creates an MBean for AMQ Diagnostic exchange")
- public DiagnosticExchangeMBean() throws JMException
- {
- super(DiagnosticExchange.this);
-
- init();
- }
-
- /**
- * Returns nothing, there can be no tabular data for this...
- *
- * @throws OpenDataException
- * @returns null
- * TODO or can there? Could this actually return all the information in one easy to read table?
- */
- public TabularData bindings() throws OpenDataException
- {
- return null;
- }
-
- /**
- * This exchange type doesn't support queues, so this method does
- * nothing.
- *
- * @param queueName the queue you'll fail to create
- * @param binding the binding you'll fail to create
- * @throws JMException an exception that will never be thrown
- */
- @Override
- public void createNewBinding(String queueName, String binding) throws JMException
- {
- // No Op
- }
-
- /**
- * This exchange type doesn't support queues.
- *
- * @see #createNewBinding(String, String)
- */
- @Override
- public void removeBinding(String queueName, String binding) throws JMException
- {
- // No Op
- }
- }
-
-
- public static final ExchangeType<DiagnosticExchange> TYPE = new ExchangeType<DiagnosticExchange>()
- {
-
- public AMQShortString getName()
- {
- return DIAGNOSTIC_EXCHANGE_CLASS;
- }
-
- public Class<DiagnosticExchange> getExchangeClass()
- {
- return DiagnosticExchange.class;
- }
-
- public DiagnosticExchange newInstance(UUID id, VirtualHost host,
- AMQShortString name,
- boolean durable,
- int ticket,
- boolean autoDelete) throws AMQException
- {
- DiagnosticExchange exch = new DiagnosticExchange();
- exch.initialise(id, host,name,durable,ticket,autoDelete);
- return exch;
- }
-
- public AMQShortString getDefaultExchangeName()
- {
- return DIAGNOSTIC_EXCHANGE_NAME ;
- }
- };
-
- public DiagnosticExchange()
- {
- super(TYPE);
- }
-
- /**
- * Creates a new MBean instance
- *
- * @return the newly created MBean
- * @throws AMQException
- * if something goes wrong
- */
- protected AbstractExchangeMBean createMBean() throws JMException
- {
- return new DiagnosticExchange.DiagnosticExchangeMBean();
- }
-
- public void registerQueue(String routingKey, AMQQueue queue, Map<String, Object> args) throws AMQException
- {
- // No op
- }
-
-
- public boolean isBound(AMQShortString routingKey, AMQQueue queue)
- {
- return false;
- }
-
- public boolean isBound(AMQShortString routingKey)
- {
- return false;
- }
-
- public boolean isBound(AMQQueue queue)
- {
- return false;
- }
-
- public boolean hasBindings()
- {
- return false;
- }
-
- public ArrayList<AMQQueue> doRoute(InboundMessage payload)
- {
- //TODO shouldn't modify messages... perhaps put a new message on the queue?
- AMQQueue q = getQueueRegistry().getQueue(new AMQShortString("diagnosticqueue"));
- ArrayList<AMQQueue> queues = new ArrayList<AMQQueue>();
- queues.add(q);
- return queues;
- }
-
-
- public boolean isBound(AMQShortString routingKey, FieldTable arguments,
- AMQQueue queue) {
- // TODO Auto-generated method stub
- return false;
- }
-
- protected void onBind(final Binding binding)
- {
- // No op
- }
-
- protected void onUnbind(final Binding binding)
- {
- // No op
- }
-}
diff --git a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java b/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java
deleted file mode 100644
index c0f325e42f..0000000000
--- a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-package org.apache.qpid.extras.exchanges.diagnostic;
-
-import java.util.UUID;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.exchange.ExchangeType;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-/**
- * Exchange type class for getting hold of the exchange.
- */
-public final class DiagnosticExchangeType implements ExchangeType<DiagnosticExchange>
-{
-
- public AMQShortString getName()
- {
- return DiagnosticExchange.DIAGNOSTIC_EXCHANGE_CLASS;
- }
-
- public Class<DiagnosticExchange> getExchangeClass()
- {
- return DiagnosticExchange.class;
- }
-
- public DiagnosticExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete)
- throws AMQException
- {
- DiagnosticExchange exch = new DiagnosticExchange();
- exch.initialise(id, host, name, durable, ticket, autoDelete);
- return exch;
- }
-
- public AMQShortString getDefaultExchangeName()
- {
- return DiagnosticExchange.DIAGNOSTIC_EXCHANGE_NAME;
- }
-}
diff --git a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java b/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java
deleted file mode 100644
index 691f4c8d0b..0000000000
--- a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java
+++ /dev/null
@@ -1,262 +0,0 @@
-package org.apache.qpid.extras.exchanges.example;
-/*
- *
- * 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.
- *
- */
-
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.server.binding.Binding;
-import org.apache.qpid.server.configuration.ConfiguredObject;
-import org.apache.qpid.server.configuration.ExchangeConfigType;
-import org.apache.qpid.server.configuration.VirtualHostConfig;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.exchange.ExchangeReferrer;
-import org.apache.qpid.server.exchange.ExchangeType;
-import org.apache.qpid.server.message.InboundMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.BaseQueue;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
-
-public class TestExchange implements Exchange
-{
-
- public void close() throws AMQException
- {
- }
-
-
-
- public void addBindingListener(final BindingListener listener)
- {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public void removeBindingListener(final BindingListener listener)
- {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public AMQShortString getNameShortString()
- {
- return null;
- }
-
- public AMQShortString getTypeShortString()
- {
- return null;
- }
-
- public boolean hasBindings()
- {
- return false;
- }
-
- public boolean isBound(String bindingKey, AMQQueue queue)
- {
- return false;
- }
-
- public boolean isBound(String bindingKey, Map<String, Object> arguments, AMQQueue queue)
- {
- return false;
- }
-
- public boolean isBound(String bindingKey)
- {
- return false;
- }
-
- public void addCloseTask(final Task task)
- {
-
- }
-
- public void removeCloseTask(final Task task)
- {
-
- }
-
- public Exchange getAlternateExchange()
- {
- return null;
- }
-
- public Map<String, Object> getArguments()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getBindingCount()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getBindingCountHigh()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getMsgReceives()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getMsgRoutes()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getByteReceives()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getByteRoutes()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public long getCreateTime()
- {
- return 0; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public void setAlternateExchange(Exchange exchange)
- {
-
- }
-
- public void removeReference(ExchangeReferrer exchange)
- {
-
- }
-
- public void addReference(ExchangeReferrer exchange)
- {
-
- }
-
- public boolean hasReferrers()
- {
- return false;
- }
-
- public void addBinding(final Binding binding)
- {
-
- }
-
- public void removeBinding(final Binding binding)
- {
-
- }
-
- public Collection<Binding> getBindings()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public void initialise(VirtualHost host, AMQShortString name, boolean durable, boolean autoDelete)
- throws AMQException
- {
- }
-
- public VirtualHostConfig getVirtualHost()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public String getName()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public ExchangeType getType()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public boolean isAutoDelete()
- {
- return false;
- }
-
- public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
- {
- return false;
- }
-
- public boolean isBound(AMQShortString routingKey, AMQQueue queue)
- {
- return false;
- }
-
- public boolean isBound(AMQShortString routingKey)
- {
- return false;
- }
-
- public boolean isBound(AMQQueue queue)
- {
- return false;
- }
-
- public UUID getId()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public ExchangeConfigType getConfigType()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public ConfiguredObject getParent()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public boolean isDurable()
- {
- return false;
- }
-
- public ArrayList<? extends BaseQueue> route(InboundMessage message)
- {
- return new ArrayList<AMQQueue>();
- }
-
- public int getTicket()
- {
- return 0;
- }
-
- public void initialise(UUID id, VirtualHost arg0, AMQShortString arg1, boolean arg2, int arg3, boolean arg4)
- throws AMQException
- {
- }
-}
diff --git a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java b/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java
deleted file mode 100644
index 5c454b9d0e..0000000000
--- a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-package org.apache.qpid.extras.exchanges.example;
-
-import java.util.UUID;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.exchange.ExchangeType;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-public class TestExchangeType implements ExchangeType
-{
-
- public Class getExchangeClass()
- {
- return TestExchange.class;
- }
-
- public AMQShortString getName()
- {
- return null;
- }
-
- public Exchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable,
- int token, boolean autoDelete)
- throws AMQException
- {
- TestExchange ex = new TestExchange();
- ex.initialise(id, host, name, durable, token, autoDelete);
- return ex;
- }
-
- public AMQShortString getDefaultExchangeName()
- {
- return new AMQShortString("test.exchange");
- }
-
-}
diff --git a/java/broker-plugins/extras/src/test/java/org/apache/qpid/server/plugins/ExtrasTest.java b/java/broker-plugins/extras/src/test/java/org/apache/qpid/server/plugins/ExtrasTest.java
deleted file mode 100644
index 458f9a1846..0000000000
--- a/java/broker-plugins/extras/src/test/java/org/apache/qpid/server/plugins/ExtrasTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.qpid.server.plugins;
-
-import junit.framework.TestCase;
-import org.apache.commons.configuration.PropertiesConfiguration;
-
-import org.apache.qpid.server.configuration.ServerConfiguration;
-import org.apache.qpid.server.exchange.ExchangeType;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.IApplicationRegistry;
-import org.apache.qpid.server.util.TestApplicationRegistry;
-
-import java.util.Map;
-
-public class ExtrasTest extends TestCase
-{
- private static final String TEST_EXCHANGE_CLASS = "org.apache.qpid.extras.exchanges.example.TestExchangeType";
-
- private static final String PLUGIN_DIRECTORY = System.getProperty("example.plugin.target");
- private static final String CACHE_DIRECTORY = System.getProperty("example.cache.target");
-
- private IApplicationRegistry _registry;
-
- @Override
- public void setUp() throws Exception
- {
- PropertiesConfiguration properties = new PropertiesConfiguration();
- properties.addProperty("plugin-directory", PLUGIN_DIRECTORY);
- properties.addProperty("cache-directory", CACHE_DIRECTORY);
- ServerConfiguration config = new ServerConfiguration(properties);
-
- // This Test requires an application Registry
- ApplicationRegistry.initialise(new TestApplicationRegistry(config));
- _registry = ApplicationRegistry.getInstance();
- }
-
- @Override
- public void tearDown() throws Exception
- {
- ApplicationRegistry.remove();
- }
-
- public void testLoadExchanges() throws Exception
- {
- PluginManager manager = _registry.getPluginManager();
- Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
- assertNotNull("No exchanges found in " + PLUGIN_DIRECTORY, exchanges);
- assertEquals("Wrong number of exchanges found in " + PLUGIN_DIRECTORY, 2, exchanges.size());
- assertNotNull("Wrong exchange found in " + PLUGIN_DIRECTORY, exchanges.get(TEST_EXCHANGE_CLASS));
- }
-
- public void testNoExchanges() throws Exception
- {
- PluginManager manager = new PluginManager("/path/to/nowhere", "/tmp", null);
- Map<String, ExchangeType<?>> exchanges = manager.getExchanges();
- assertTrue("Exchanges found", exchanges.isEmpty());
- }
-}
diff --git a/java/broker-plugins/firewall/MANIFEST.MF b/java/broker-plugins/firewall/MANIFEST.MF
index 6ceea119da..a302921d03 100644
--- a/java/broker-plugins/firewall/MANIFEST.MF
+++ b/java/broker-plugins/firewall/MANIFEST.MF
@@ -12,12 +12,10 @@ Bundle-ClassPath: .
Bundle-ActivationPolicy: lazy
Import-Package: org.apache.qpid,
org.apache.qpid.framing,
- org.apache.qpid.junit.extensions.util,
org.apache.qpid.protocol,
org.apache.qpid.server.configuration,
org.apache.qpid.server.configuration.plugins,
org.apache.qpid.server.exchange,
- org.apache.qpid.server.management,
org.apache.qpid.server.plugins,
org.apache.qpid.server.queue,
org.apache.qpid.server.security,
diff --git a/java/broker-plugins/firewall/build.xml b/java/broker-plugins/firewall/build.xml
index 576435de7f..6ae6a35b89 100644
--- a/java/broker-plugins/firewall/build.xml
+++ b/java/broker-plugins/firewall/build.xml
@@ -17,13 +17,18 @@
- under the License.
-->
<project name="Qpid Broker-Plugins Firewall" default="build">
- <property name="module.depends" value="common broker broker-plugins" />
+ <property name="module.depends" value="common broker" />
<property name="module.test.depends" value="test broker/test common/test management/common" />
<property name="module.manifest" value="MANIFEST.MF" />
<property name="module.plugin" value="true" />
+ <property name="module.genpom" value="true"/>
+ <property name="module.genpom.args" value="-Sqpid-common=provided -Sqpid-broker=provided"/>
+
+ <property name="broker-plugins-firewall.libs" value=""/>
<import file="../../module.xml" />
<target name="bundle" depends="bundle-tasks" />
+
</project>
diff --git a/java/broker-plugins/management-http/MANIFEST.MF b/java/broker-plugins/management-http/MANIFEST.MF
new file mode 100644
index 0000000000..cca10d3f89
--- /dev/null
+++ b/java/broker-plugins/management-http/MANIFEST.MF
@@ -0,0 +1,70 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Qpid Broker-Plugins Management HTTP
+Bundle-SymbolicName: broker-plugins-management-http
+Bundle-Description: HTTP management plugin for Qpid.
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Bundle-Version: 1.0.0
+Bundle-Activator: org.apache.qpid.server.management.plugin.ManagementActivator
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: .
+Bundle-ActivationPolicy: lazy
+Import-Package: org.apache.qpid,
+ org.apache.qpid.framing,
+ org.apache.qpid.protocol,
+ org.apache.qpid.common,
+ org.apache.qpid.server.security.auth,
+ org.apache.qpid.server.security.auth.manager,
+ org.apache.qpid.server.security.auth.sasl,
+ org.apache.qpid.server.binding,
+ org.apache.qpid.server.exchange,
+ org.apache.qpid.server.logging,
+ org.apache.qpid.server.message,
+ org.apache.qpid.server.model,
+ org.apache.qpid.server.model.adapter,
+ org.apache.qpid.server.model.impl,
+ org.apache.qpid.server.configuration,
+ org.apache.qpid.server.configuration.plugins,
+ org.apache.qpid.server.connection,
+ org.apache.qpid.server.plugins,
+ org.apache.qpid.server.protocol,
+ org.apache.qpid.server.queue,
+ org.apache.qpid.server.subscription,
+ org.apache.qpid.server.registry,
+ org.apache.qpid.server.security,
+ org.apache.qpid.server.security.access,
+ org.apache.qpid.server.stats,
+ org.apache.qpid.server.virtualhost,
+ org.apache.qpid.util,
+ org.eclipse.jetty.server;version=7.6.3,
+ org.eclipse.jetty.server.session;version=7.6.3,
+ org.eclipse.jetty.server.ssl;version=7.6.3,
+ org.eclipse.jetty.server.nio;version=7.6.3,
+ org.eclipse.jetty.security;version=7.6.3,
+ org.eclipse.jetty.http;version=7.6.3,
+ org.eclipse.jetty.io;version=7.6.3,
+ org.eclipse.jetty.io.nio;version=7.6.3,
+ org.eclipse.jetty.servlet;version=7.6.3,
+ org.eclipse.jetty.util.ssl;version=7.6.3,
+ org.apache.commons.codec;version=1.3.0,
+ org.apache.commons.codec.binary;version=1.3.0,
+ org.apache.commons.configuration;version=1.0.0,
+ org.apache.commons.lang;version=1.0.0,
+ org.apache.commons.lang.builder;version=1.0.0,
+ org.apache.log4j;version=1.0.0,
+ org.codehaus.jackson;version=1.9.0,
+ org.codehaus.jackson.map;version=1.9.0,
+ javax.crypto,
+ javax.crypto.spec,
+ javax.security.auth,
+ javax.security.auth.callback,
+ javax.security.sasl,
+ javax.servlet,
+ javax.servlet.http,
+ javax.management;version=1.0.0,
+ javax.management.openmbean;version=1.0.0,
+ org.osgi.util.tracker;version=1.0.0,
+ org.osgi.framework;version=1.3
+Private-Package: org.apache.qpid.server.management.plugin.impl
+Export-Package: org.apache.qpid.server.management.plugin;uses:="org.osgi.framework"
diff --git a/java/broker-plugins/management-http/build.xml b/java/broker-plugins/management-http/build.xml
new file mode 100644
index 0000000000..b792cb292e
--- /dev/null
+++ b/java/broker-plugins/management-http/build.xml
@@ -0,0 +1,57 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -->
+<project name="Qpid Broker-Plugins Management HTTP" default="build">
+
+ <condition property="systests.optional.depends" value="bdbstore" else="">
+ <or>
+ <and>
+ <contains string="${modules.opt}" substring="bdbstore"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ <and>
+ <istrue value="${optional}"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ </or>
+ </condition>
+
+ <property name="module.depends" value="common broker" />
+ <property name="module.test.depends" value="systests test broker/test common/test management/common client ${systests.optional.depends}" />
+
+ <property name="module.manifest" value="MANIFEST.MF" />
+ <property name="module.plugin" value="true" />
+ <property name="module.genpom" value="true"/>
+ <property name="module.genpom.args" value="-Sqpid-common=provided -Sqpid-broker=provided"/>
+
+ <property name="broker-plugins-management-http.libs" value=""/>
+
+ <import file="../../module.xml" />
+
+ <target name="precompile">
+ <unwar src="${project.root}/${dojo}" dest="${module.classes}/resources/dojo">
+ <patternset>
+ <exclude name="META-INF/**"/>
+ <exclude name="WEB-INF/**"/>
+ <exclude name="**/*.uncompressed.js"/>
+ </patternset>
+ </unwar>
+ </target>
+
+ <target name="bundle" depends="bundle-tasks" />
+</project>
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java
new file mode 100644
index 0000000000..c2f9b73b54
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java
@@ -0,0 +1,248 @@
+/*
+ *
+ * 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.management.plugin;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet;
+import org.apache.qpid.server.management.plugin.servlet.FileServlet;
+import org.apache.qpid.server.management.plugin.servlet.api.ExchangesServlet;
+import org.apache.qpid.server.management.plugin.servlet.api.VhostsServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.MessageContentServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.MessageServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.RestServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.SaslServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.StructureServlet;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.Session;
+import org.apache.qpid.server.model.Transport;
+import org.apache.qpid.server.model.User;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslSocketConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class Management
+{
+
+ private final Logger _logger = Logger.getLogger(Management.class);
+
+ private Broker _broker;
+
+ private Collection<Server> _servers = new ArrayList<Server>();
+
+ public Management() throws ConfigurationException, IOException
+ {
+ _broker = ApplicationRegistry.getInstance().getBroker();
+
+ Collection<Port> ports = _broker.getPorts();
+ int httpPort = -1, httpsPort = -1;
+ for (Port port : ports)
+ {
+ if (port.getProtocols().contains(Protocol.HTTP))
+ {
+ if (port.getTransports().contains(Transport.TCP))
+ {
+ httpPort = port.getPort();
+ }
+ }
+ if (port.getProtocols().contains(Protocol.HTTPS))
+ {
+ if (port.getTransports().contains(Transport.SSL))
+ {
+ httpsPort = port.getPort();
+ }
+ }
+ }
+
+ if (httpPort != -1 || httpsPort != -1)
+ {
+ _servers.add(createServer(httpPort, httpsPort));
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(_servers.size() + " server(s) defined");
+ }
+ }
+ else
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Cannot create web server as neither HTTP nor HTTPS port specified");
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Server createServer(int port, int sslPort) throws IOException, ConfigurationException
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Starting up web server on" + (port == -1 ? "" : " HTTP port " + port)
+ + (sslPort == -1 ? "" : " HTTPS port " + sslPort));
+ }
+
+ Server server = new Server();
+
+ if (port != -1)
+ {
+ SelectChannelConnector connector = new SelectChannelConnector();
+ connector.setPort(port);
+ if (sslPort != -1)
+ {
+ connector.setConfidentialPort(sslPort);
+ }
+ server.addConnector(connector);
+ }
+
+ if (sslPort != -1)
+ {
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ String keyStorePath = getKeyStorePath(appRegistry);
+
+ SslContextFactory factory = new SslContextFactory();
+ factory.setKeyStorePath(keyStorePath);
+ factory.setKeyStorePassword(appRegistry.getConfiguration().getManagementKeyStorePassword());
+
+ SslSocketConnector connector = new SslSocketConnector(factory);
+ connector.setPort(sslPort);
+ server.addConnector(connector);
+ }
+
+ ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS);
+ root.setContextPath("/");
+ server.setHandler(root);
+
+ root.addServlet(new ServletHolder(new VhostsServlet(_broker)), "/api/vhosts/*");
+ root.addServlet(new ServletHolder(new ExchangesServlet(_broker)), "/api/exchanges/*");
+
+ addRestServlet(root, "broker");
+ addRestServlet(root, "virtualhost", VirtualHost.class);
+ addRestServlet(root, "authenticationprovider", AuthenticationProvider.class);
+ addRestServlet(root, "user", AuthenticationProvider.class, User.class);
+ addRestServlet(root, "exchange", VirtualHost.class, Exchange.class);
+ addRestServlet(root, "queue", VirtualHost.class, Queue.class);
+ addRestServlet(root, "connection", VirtualHost.class, Connection.class);
+ addRestServlet(root, "binding", VirtualHost.class, Exchange.class, Queue.class, Binding.class);
+ addRestServlet(root, "port", Port.class);
+ addRestServlet(root, "session", VirtualHost.class, Connection.class, Session.class);
+
+ root.addServlet(new ServletHolder(new StructureServlet(_broker)), "/rest/structure");
+ root.addServlet(new ServletHolder(new MessageServlet(_broker)), "/rest/message/*");
+ root.addServlet(new ServletHolder(new MessageContentServlet(_broker)), "/rest/message-content/*");
+
+ root.addServlet(new ServletHolder(new LogRecordsServlet(_broker)), "/rest/logrecords");
+
+ root.addServlet(new ServletHolder(new SaslServlet(_broker)), "/rest/sasl");
+
+ root.addServlet(new ServletHolder(new DefinedFileServlet("management.html")), "/management");
+
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.js");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.css");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.html");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.png");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.gif");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpg");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpeg");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.json");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.txt");
+ root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.xsl");
+
+ final SessionManager sessionManager = root.getSessionHandler().getSessionManager();
+
+ sessionManager.setMaxInactiveInterval(60 * 15);
+
+ return server;
+ }
+
+ private void addRestServlet(ServletContextHandler root, String name, Class<? extends ConfiguredObject>... hierarchy)
+ {
+ root.addServlet(new ServletHolder(new RestServlet(_broker, hierarchy)), "/rest/" + name + "/*");
+ }
+
+ public void start() throws Exception
+ {
+ for (Server server : _servers)
+ {
+ server.start();
+ }
+ }
+
+ public void stop() throws Exception
+ {
+ for (Server server : _servers)
+ {
+ server.stop();
+ }
+ }
+
+ private String getKeyStorePath(IApplicationRegistry appRegistry) throws ConfigurationException, FileNotFoundException
+ {
+ String keyStorePath = null;
+ if (System.getProperty("javax.net.ssl.keyStore") != null)
+ {
+ keyStorePath = System.getProperty("javax.net.ssl.keyStore");
+ }
+ else
+ {
+ keyStorePath = appRegistry.getConfiguration().getManagementKeyStorePath();
+ }
+
+ if (keyStorePath == null)
+ {
+ throw new ConfigurationException("Management SSL keystore path not defined, unable to start SSL protected HTTP connector");
+ }
+ else
+ {
+ File ksf = new File(keyStorePath);
+ if (!ksf.exists())
+ {
+ throw new FileNotFoundException("Cannot find management SSL keystore file: " + ksf);
+ }
+ if (!ksf.canRead())
+ {
+ throw new FileNotFoundException("Cannot read management SSL keystore file: " + ksf + ". Check permissions.");
+ }
+ }
+ return keyStorePath;
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java
new file mode 100644
index 0000000000..09b7e08bfb
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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.management.plugin;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class ManagementActivator implements BundleActivator
+{
+ private static final Logger _logger = Logger.getLogger(ManagementActivator.class);
+
+
+ private BundleContext _ctx;
+ private String _bundleName;
+ private Management _managementService;
+
+
+ public void start(final BundleContext ctx) throws Exception
+ {
+ _ctx = ctx;
+ if (!ApplicationRegistry.getInstance().getConfiguration().getHTTPManagementEnabled()
+ && !ApplicationRegistry.getInstance().getConfiguration().getHTTPSManagementEnabled())
+ {
+ _logger.info("Management plugin is disabled!");
+ ctx.getBundle().uninstall();
+ return;
+ }
+ _managementService = new Management();
+ _managementService.start();
+ _bundleName = ctx.getBundle().getSymbolicName();
+
+ // register the service
+ _logger.info("Registering management plugin: " + _bundleName);
+ _ctx.registerService(Management.class.getName(), _managementService, null);
+ _ctx.registerService(ConfigurationPluginFactory.class.getName(), ManagementConfiguration.FACTORY, null);
+ }
+
+ public void stop(final BundleContext bundleContext) throws Exception
+ {
+ if (_managementService != null)
+ {
+ _logger.info("Stopping management plugin: " + _bundleName);
+
+ _managementService.stop();
+
+ // null object references
+ _managementService = null;
+ }
+ _ctx = null;
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java
new file mode 100644
index 0000000000..3866da8f89
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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.management.plugin;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+
+import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ManagementConfiguration extends ConfigurationPlugin
+{
+ CompositeConfiguration _finalConfig;
+
+ public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory()
+ {
+ public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException
+ {
+ ConfigurationPlugin instance = new ManagementConfiguration();
+ instance.setConfiguration(path, config);
+ return instance;
+ }
+
+ public List<String> getParentPaths()
+ {
+ return Arrays.asList("management");
+ }
+ };
+
+ public String[] getElementsProcessed()
+ {
+ return new String[] { "" };
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _finalConfig;
+ }
+
+
+ @Override
+ public void validateConfiguration() throws ConfigurationException
+ {
+ // Valid Configuration either has xml links to new files
+ _finalConfig = new CompositeConfiguration(getConfig());
+ List subFiles = getConfig().getList("xml[@fileName]");
+ for (Object subFile : subFiles)
+ {
+ _finalConfig.addConfiguration(new XMLConfiguration((String) subFile));
+ }
+
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java
new file mode 100644
index 0000000000..d8a8395550
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.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.management.plugin.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class DefinedFileServlet extends HttpServlet
+{
+
+ private static final String FILENAME_INIT_PARAMETER = "filename";
+
+ private String _filename;
+
+ public DefinedFileServlet()
+ {
+ super();
+ }
+
+ public DefinedFileServlet(String filename)
+ {
+ _filename = filename;
+ }
+
+ @Override
+ public void init() throws ServletException
+ {
+ ServletConfig config = getServletConfig();
+ String fileName = config.getInitParameter(FILENAME_INIT_PARAMETER);
+ if (fileName != null && !"".equals(fileName))
+ {
+ _filename = fileName;
+ }
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ final ServletOutputStream output = response.getOutputStream();
+ InputStream fileInput = getClass().getResourceAsStream("/resources/"+_filename);
+
+ if(fileInput != null)
+ {
+ byte[] buffer = new byte[1024];
+ response.setStatus(HttpServletResponse.SC_OK);
+ int read = 0;
+
+ while((read = fileInput.read(buffer)) > 0)
+ {
+ output.write(buffer, 0, read);
+ }
+ }
+ else
+ {
+ response.sendError(404, "unknown file: "+ _filename);
+ }
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java
new file mode 100644
index 0000000000..f8ca082d79
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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.management.plugin.servlet;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class FileServlet extends HttpServlet
+{
+ public static final FileServlet INSTANCE = new FileServlet();
+
+ private static final Map<String, String> CONTENT_TYPES;
+
+ static
+ {
+
+ Map<String, String> contentTypes = new HashMap<String, String>();
+ contentTypes.put("js", "application/javascript");
+ contentTypes.put("html", "text/html");
+ contentTypes.put("css", "text/css");
+ contentTypes.put("json", "application/json");
+ contentTypes.put("jpg", "image/jpg");
+ contentTypes.put("png", "image/png");
+ contentTypes.put("gif", "image/gif");
+ CONTENT_TYPES = Collections.unmodifiableMap(contentTypes);
+ }
+
+
+ public FileServlet()
+ {
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ String filename = request.getServletPath();
+ if(filename.contains("."))
+ {
+ String suffix = filename.substring(filename.lastIndexOf('.')+1);
+ String contentType = CONTENT_TYPES.get(suffix);
+ if(contentType != null)
+ {
+ response.setContentType(contentType);
+ }
+ }
+ URL resourceURL = getClass().getResource("/resources" + filename);
+ if(resourceURL != null)
+ {
+ response.setStatus(HttpServletResponse.SC_OK);
+ InputStream fileInput = resourceURL.openStream();
+ try
+ {
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ ServletOutputStream output = response.getOutputStream();
+ try
+ {
+ while((read = fileInput.read(buffer)) != -1)
+ {
+ output.write(buffer, 0, read);
+ }
+ }
+ finally
+ {
+ output.close();
+ }
+ }
+ finally
+ {
+ fileInput.close();
+ }
+ }
+ else
+ {
+ response.sendError(404, "unknown file: "+ filename);
+ }
+
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java
new file mode 100644
index 0000000000..a3c5ec68a2
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java
@@ -0,0 +1,208 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.api;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.ObjectReader;
+
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Exchange;
+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.registry.ApplicationRegistry;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+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;
+
+public class ExchangesServlet extends HttpServlet
+{
+
+
+ private Broker _broker;
+
+ public ExchangesServlet()
+ {
+ super();
+ _broker = ApplicationRegistry.getInstance().getBroker();
+ }
+
+ public ExchangesServlet(Broker broker)
+ {
+ _broker = broker;
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ Collection<VirtualHost> vhosts = _broker.getVirtualHosts();
+ Collection<Exchange> exchanges = new ArrayList<Exchange>();
+ Collection<Map<String,Object>> outputObject = new ArrayList<Map<String,Object>>();
+
+ final PrintWriter writer = response.getWriter();
+
+ ObjectMapper mapper = new ObjectMapper();
+ String vhostName = null;
+ String exchangeName = null;
+
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ String[] parts = path.split("/");
+ vhostName = parts.length == 0 ? "" : parts[0];
+ if(parts.length > 1)
+ {
+ exchangeName = parts[1];
+ }
+ }
+
+ for(VirtualHost vhost : vhosts)
+ {
+ if(vhostName == null || vhostName.equals(vhost.getName()))
+ {
+ for(Exchange exchange : vhost.getExchanges())
+ {
+ if(exchangeName == null || exchangeName.equals(exchange.getName()))
+ {
+ outputObject.add(convertToObject(exchange));
+ if(exchangeName != null)
+ {
+ break;
+ }
+ }
+ }
+ if(vhostName != null)
+ {
+ break;
+ }
+ }
+ }
+
+ mapper.writeValue(writer, outputObject);
+
+ }
+
+ private Map<String,Object> convertToObject(final Exchange exchange)
+ {
+ Map<String, Object> object = new LinkedHashMap<String, Object>();
+ object.put("name",exchange.getName());
+ object.put("type", exchange.getExchangeType());
+ object.put("durable", exchange.isDurable());
+ object.put("auto-delete", exchange.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE);
+
+ Map<String,Object> arguments = new HashMap<String, Object>();
+ for(String key : exchange.getAttributeNames())
+ {
+ if(!key.equals(Exchange.TYPE))
+ {
+ arguments.put(key, exchange.getAttribute(key));
+ }
+ }
+ object.put("arguments", arguments);
+ return object;
+ }
+
+ protected void doPut(final HttpServletRequest request, final HttpServletResponse response)
+ throws ServletException, IOException
+ {
+
+ response.setContentType("application/json");
+
+
+ String vhostName = null;
+ String exchangeName = null;
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ String[] parts = path.split("/");
+ vhostName = parts.length == 0 ? "" : parts[0];
+ if(parts.length > 1)
+ {
+ exchangeName = parts[1];
+ }
+ }
+ if(vhostName == null)
+ {
+ response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ else if (exchangeName == null)
+ {
+ response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ else
+ {
+ VirtualHost vhost = null;
+ for(VirtualHost host : _broker.getVirtualHosts())
+ {
+ if(host.getName().equals(vhostName))
+ {
+ vhost = host;
+ }
+ }
+ if(vhost == null)
+ {
+ response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ ObjectMapper mapper = new ObjectMapper();
+ Map<String,Object> exchangeObject = mapper.readValue(request.getInputStream(), LinkedHashMap.class);
+
+ final boolean isDurable = exchangeObject.get("durable") instanceof Boolean
+ && ((Boolean)exchangeObject.get("durable"));
+ final boolean isAutoDelete = exchangeObject.get("auto_delete") instanceof Boolean
+ && ((Boolean)exchangeObject.get("auto_delete"));
+
+ final String type = (String) exchangeObject.get("type");
+ final Map<String, Object> attributes = new HashMap<String, Object>(exchangeObject);
+ attributes.remove("durable");
+ attributes.remove("auto_delete");
+ attributes.remove("type");
+
+ vhost.createExchange(exchangeName, State.ACTIVE, isDurable,
+ isAutoDelete ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT,
+ 0l,
+ type,
+ attributes);
+ }
+
+
+
+ }
+
+
+
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java
new file mode 100644
index 0000000000..b2c0fcfe52
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.api;
+
+import org.codehaus.jackson.map.ObjectMapper;
+
+import org.apache.qpid.common.QpidProperties;
+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.registry.ApplicationRegistry;
+
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.*;
+
+public class VhostsServlet extends HttpServlet
+{
+
+
+ private Broker _broker;
+
+ public VhostsServlet()
+ {
+ super();
+ _broker = ApplicationRegistry.getInstance().getBroker();
+ }
+
+ public VhostsServlet(Broker broker)
+ {
+ _broker = broker;
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+System.out.println("Get /api/vhosts");
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ Collection<VirtualHost> vhosts = _broker.getVirtualHosts();
+
+
+
+ final PrintWriter writer = response.getWriter();
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ if(request.getPathInfo() == null || request.getPathInfo().length()==0)
+ {
+
+ LinkedHashMap<String, Object> vhostObject = new LinkedHashMap<String, Object>();
+ List<Map> vhostList = new ArrayList<Map>();
+
+ for(VirtualHost vhost : vhosts)
+ {
+ vhostList.add(Collections.singletonMap("name", vhost.getName()));
+ }
+ mapper.writeValue(writer, vhostList);
+ }
+ else
+ {
+ LinkedHashMap<String, Object> vhostObject = new LinkedHashMap<String, Object>();
+ String vhostName = request.getPathInfo().substring(1);
+
+ for(VirtualHost vhost : vhosts)
+ {
+ if(vhostName.equals(vhost.getName()))
+ {
+ vhostObject.put("name", vhost.getName());
+ break;
+ }
+ }
+ mapper.writeValue(writer, vhostObject);
+ }
+ }
+
+
+ protected void doPut(final HttpServletRequest request, final HttpServletResponse response)
+ throws ServletException, IOException
+ {
+
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String vhostName = request.getPathInfo().substring(1);
+ _broker.createVirtualHost(vhostName, State.ACTIVE, true, LifetimePolicy.PERMANENT, 0L, Collections.EMPTY_MAP);
+ }
+
+
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
new file mode 100644
index 0000000000..a76bd98179
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
@@ -0,0 +1,208 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.Principal;
+import java.util.Collections;
+import javax.security.auth.Subject;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+
+public abstract class AbstractServlet extends HttpServlet
+{
+ private final Broker _broker;
+
+ protected AbstractServlet()
+ {
+ super();
+ _broker = ApplicationRegistry.getInstance().getBroker();
+ }
+
+ protected AbstractServlet(Broker broker)
+ {
+ _broker = broker;
+ }
+
+ @Override
+ protected final void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException
+ {
+ setAuthorizedSubject(request);
+ try
+ {
+ onGet(request, resp);
+ }
+ finally
+ {
+ clearAuthorizedSubject();
+ }
+ }
+
+ protected void onGet(HttpServletRequest request, HttpServletResponse resp) throws IOException, ServletException
+ {
+ super.doGet(request, resp);
+ }
+
+ private void clearAuthorizedSubject()
+ {
+ org.apache.qpid.server.security.SecurityManager.setThreadSubject(null);
+ }
+
+
+ private void setAuthorizedSubject(HttpServletRequest request)
+ {
+ HttpSession session = request.getSession(true);
+ Subject subject = (Subject) session.getAttribute("subject");
+
+ if(subject == null)
+ {
+ Principal principal = request.getUserPrincipal();
+ if(principal != null)
+ {
+ subject = new Subject(false, Collections.singleton(principal),Collections.emptySet(),
+ Collections.emptySet());
+ }
+ else
+ {
+ String header = request.getHeader("Authorization");
+
+ /*
+ * TODO - Should configure whether basic authentication is allowed... and in particular whether it
+ * should be allowed over non-ssl connections
+ * */
+
+ if (header != null)
+ {
+ String[] tokens = header.split("\\s");
+ if(tokens.length >= 2
+ && "BASIC".equalsIgnoreCase(tokens[0]))
+ {
+ String[] credentials = (new String(Base64.decodeBase64(tokens[1].getBytes()))).split(":",2);
+ if(credentials.length == 2)
+ {
+ SocketAddress address = getSocketAddress(request);
+ AuthenticationManager authenticationManager =
+ ApplicationRegistry.getInstance().getAuthenticationManager(address);
+ AuthenticationResult authResult =
+ authenticationManager.authenticate(credentials[0], credentials[1]);
+ subject = authResult.getSubject();
+
+ }
+ }
+ }
+ }
+ }
+ if (subject == null)
+ {
+ subject = AnonymousAuthenticationManager.ANONYMOUS_SUBJECT;
+ }
+ org.apache.qpid.server.security.SecurityManager.setThreadSubject(subject);
+
+ }
+
+ protected Subject getSubject(HttpSession session)
+ {
+ return (Subject)session.getAttribute("subject");
+ }
+
+ @Override
+ protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ setAuthorizedSubject(req);
+ try
+ {
+ onPost(req, resp);
+ }
+ finally
+ {
+ clearAuthorizedSubject();
+ }
+
+ }
+
+ protected void onPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ super.doPost(req, resp);
+ }
+
+ @Override
+ protected final void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ setAuthorizedSubject(req);
+ try
+ {
+ onPut(req, resp);
+
+ }
+ finally
+ {
+ clearAuthorizedSubject();
+ }
+ }
+
+ protected void onPut(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
+ {
+ super.doPut(req,resp);
+ }
+
+ @Override
+ protected final void doDelete(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException
+ {
+ setAuthorizedSubject(req);
+ try
+ {
+ onDelete(req, resp);
+ }
+ finally
+ {
+ clearAuthorizedSubject();
+ }
+ }
+
+ protected void onDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ super.doDelete(req, resp);
+ }
+
+
+ protected Broker getBroker()
+ {
+ return _broker;
+ }
+
+ protected SocketAddress getSocketAddress(HttpServletRequest request)
+ {
+ return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort());
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java
new file mode 100644
index 0000000000..3d862ce321
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.util.Comparator;
+import java.util.Map;
+
+class KeyComparator implements Comparator<Map>
+{
+ private String _key;
+
+ public KeyComparator(final String key)
+ {
+ _key = key;
+ }
+
+ public int compare(final Map o1, final Map o2)
+ {
+ Comparable left = (Comparable) o1.get(_key);
+ Comparable right = (Comparable) o2.get(_key);
+
+ int result;
+ if(left == null)
+ {
+ result = right == null ? 0 : -1;
+ }
+ else
+ {
+ result = left.compareTo(right);
+ }
+
+ return result;
+
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java
new file mode 100644
index 0000000000..404793b592
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.qpid.server.logging.LogRecorder;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+public class LogRecordsServlet extends AbstractServlet
+{
+ public LogRecordsServlet()
+ {
+ super(ApplicationRegistry.getInstance().getBroker());
+ }
+
+ public LogRecordsServlet(Broker broker)
+ {
+ super(broker);
+ }
+
+ @Override
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ ApplicationRegistry applicationRegistry = (ApplicationRegistry) ApplicationRegistry.getInstance();
+ List<Map<String,Object>> logRecords = new ArrayList<Map<String, Object>>();
+
+ for(LogRecorder.Record record : applicationRegistry.getLogRecorder())
+ {
+ logRecords.add(logRecordToObject(record));
+ }
+
+ final PrintWriter writer = response.getWriter();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, logRecords);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ }
+
+ private Map<String, Object> logRecordToObject(LogRecorder.Record record)
+ {
+ Map<String, Object> recordMap = new LinkedHashMap<String, Object>();
+ recordMap.put("id",record.getId());
+ recordMap.put("timestamp", record.getTimestamp());
+ recordMap.put("level", record.getLevel());
+ recordMap.put("thread", record.getThreadName());
+ recordMap.put("logger", record.getLogger());
+ recordMap.put("message", record.getMessage());
+ return recordMap;
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java
new file mode 100644
index 0000000000..84d987813b
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.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.management.plugin.servlet.rest;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+
+class MapComparator implements Comparator<Map>
+{
+ private Comparator<Map>[] _sortKeys;
+
+ public MapComparator(final String[] sortKeys)
+ {
+ _sortKeys = parseKeys(sortKeys);
+ }
+
+ private static Comparator<Map>[] parseKeys(final String[] sortKeys)
+ {
+ Comparator<Map>[] comparators = new Comparator[sortKeys.length];
+ for(int i = 0; i < sortKeys.length; i++)
+ {
+ String key = sortKeys[i];
+
+ if(key.startsWith("+") || key.startsWith(" "))
+ {
+ comparators[i] = new KeyComparator(key.substring(1));
+ }
+ else if(key.startsWith("-"))
+ {
+ comparators[i] = Collections.reverseOrder(new KeyComparator(key.substring(1)));
+ }
+ else
+ {
+ comparators[i] = new KeyComparator(key);
+ }
+ }
+ return comparators;
+ }
+
+
+ public int compare(final Map o1, final Map o2)
+ {
+ int result = 0;
+ for(int i = 0; i < _sortKeys.length; i++)
+ {
+ result = _sortKeys[i].compare(o1, o2);
+ if(result != 0)
+ {
+ return result;
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
new file mode 100644
index 0000000000..bc87f0bcc5
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
@@ -0,0 +1,176 @@
+/*
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.message.MessageReference;
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.QueueEntryVisitor;
+
+public class MessageContentServlet extends AbstractServlet
+{
+ public MessageContentServlet()
+ {
+ super();
+ }
+
+ public MessageContentServlet(Broker broker)
+ {
+ super(broker);
+ }
+
+ @Override
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2)
+ {
+ getMessageContent(request, response);
+ }
+
+ }
+
+ private void getMessageContent(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ Queue queue = getQueueFromRequest(request);
+ String path[] = request.getPathInfo().substring(1).split("/");
+ MessageFinder finder = new MessageFinder(Long.parseLong(path[2]));
+ queue.visit(finder);
+ if(finder.isFound())
+ {
+ response.setContentType(finder.getMimeType());
+ response.setContentLength((int) finder.getSize());
+ response.getOutputStream().write(finder.getContent());
+
+ }
+
+ }
+
+ private Queue getQueueFromRequest(HttpServletRequest request)
+ {
+ List<String> names = new ArrayList<String>();
+ // TODO - validation that there is a vhost and queue and only those in the path
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ names.addAll(Arrays.asList(path.split("/")));
+ }
+ String vhostName = names.get(0);
+ String queueName = names.get(1);
+
+ VirtualHost vhost = null;
+
+ for(VirtualHost vh : getBroker().getVirtualHosts())
+ {
+ if(vh.getName().equals(vhostName))
+ {
+ vhost = vh;
+ break;
+ }
+ }
+
+ return getQueueFromVirtualHost(queueName, vhost);
+ }
+
+ private Queue getQueueFromVirtualHost(String queueName, VirtualHost vhost)
+ {
+ Queue queue = null;
+
+ for(Queue q : vhost.getQueues())
+ {
+ if(q.getName().equals(queueName))
+ {
+ queue = q;
+ break;
+ }
+ }
+ return queue;
+ }
+
+ private class MessageFinder implements QueueEntryVisitor
+ {
+ private final long _messageNumber;
+ private String _mimeType;
+ private long _size;
+ private byte[] _content;
+ private boolean _found;
+
+ private MessageFinder(long messageNumber)
+ {
+ _messageNumber = messageNumber;
+ }
+
+
+ public boolean visit(QueueEntry entry)
+ {
+ ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ if(_messageNumber == message.getMessageNumber())
+ {
+ MessageReference reference = message.newReference();
+
+ _mimeType = message.getMessageHeader().getMimeType();
+ _size = message.getSize();
+ _content = new byte[(int)_size];
+ _found = true;
+ message.getContent(ByteBuffer.wrap(_content),0);
+ reference.release();
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+ public String getMimeType()
+ {
+ return _mimeType;
+ }
+
+ public long getSize()
+ {
+ return _size;
+ }
+
+ public byte[] getContent()
+ {
+ return _content;
+ }
+
+ public boolean isFound()
+ {
+ return _found;
+ }
+ }
+
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java
new file mode 100644
index 0000000000..6e7bc1d935
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java
@@ -0,0 +1,502 @@
+/*
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.message.MessageReference;
+import org.apache.qpid.server.message.ServerMessage;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.QueueEntryVisitor;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.security.SecurityManager;
+import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.subscription.Subscription;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+public class MessageServlet extends AbstractServlet
+{
+ private static final Logger LOGGER = Logger.getLogger(MessageServlet.class);
+
+ public MessageServlet()
+ {
+ super();
+ }
+
+ public MessageServlet(Broker broker)
+ {
+ super(broker);
+ }
+
+ @Override
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2)
+ {
+ getMessageContent(request, response);
+ }
+ else
+ {
+ getMessageList(request, response);
+ }
+
+ }
+
+ private void getMessageContent(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ Queue queue = getQueueFromRequest(request);
+ String path[] = request.getPathInfo().substring(1).split("/");
+ MessageFinder messageFinder = new MessageFinder(Long.parseLong(path[2]));
+ queue.visit(messageFinder);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ final PrintWriter writer = response.getWriter();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, messageFinder.getMessageObject());
+ }
+
+ private void getMessageList(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ Queue queue = getQueueFromRequest(request);
+
+ int first = -1;
+ int last = -1;
+ String range = request.getHeader("Range");
+ if(range != null)
+ {
+ String[] boundaries = range.split("=")[1].split("-");
+ first = Integer.parseInt(boundaries[0]);
+ last = Integer.parseInt(boundaries[1]);
+ }
+ final MessageCollector messageCollector = new MessageCollector(first, last);
+ queue.visit(messageCollector);
+
+ response.setContentType("application/json");
+ final List<Map<String, Object>> messages = messageCollector.getMessages();
+ int queueSize = ((Number) queue.getStatistics().getStatistic(Queue.QUEUE_DEPTH_MESSAGES)).intValue();
+ String min = messages.isEmpty() ? "0" : messages.get(0).get("position").toString();
+ String max = messages.isEmpty() ? "0" : messages.get(messages.size()-1).get("position").toString();
+ response.setHeader("Content-Range", (min + "-" + max + "/" + queueSize));
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ final PrintWriter writer = response.getWriter();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, messages);
+ }
+
+ private Queue getQueueFromRequest(HttpServletRequest request)
+ {
+ List<String> names = new ArrayList<String>();
+ // TODO - validation that there is a vhost and queue and only those in the path
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ names.addAll(Arrays.asList(path.split("/")));
+ }
+ String vhostName = names.get(0);
+ String queueName = names.get(1);
+
+ VirtualHost vhost = null;
+
+ for(VirtualHost vh : getBroker().getVirtualHosts())
+ {
+ if(vh.getName().equals(vhostName))
+ {
+ vhost = vh;
+ break;
+ }
+ }
+
+ return getQueueFromVirtualHost(queueName, vhost);
+ }
+
+ private Queue getQueueFromVirtualHost(String queueName, VirtualHost vhost)
+ {
+ Queue queue = null;
+
+ for(Queue q : vhost.getQueues())
+ {
+
+ if(q.getName().equals(queueName))
+ {
+ queue = q;
+ break;
+ }
+ }
+ return queue;
+ }
+
+ private abstract static class QueueEntryTransaction implements VirtualHost.TransactionalOperation
+ {
+ private final Queue _sourceQueue;
+ private final List _messageIds;
+
+ protected QueueEntryTransaction(Queue sourceQueue, List messageIds)
+ {
+ _sourceQueue = sourceQueue;
+ _messageIds = messageIds;
+ }
+
+
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+
+ _sourceQueue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ final ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ final long messageId = message.getMessageNumber();
+ if (_messageIds.remove(messageId) || (messageId <= (long) Integer.MAX_VALUE
+ && _messageIds.remove(Integer.valueOf((int)messageId))))
+ {
+ updateEntry(entry, txn);
+ }
+ }
+ return _messageIds.isEmpty();
+ }
+ });
+ }
+
+
+ protected abstract void updateEntry(QueueEntry entry, VirtualHost.Transaction txn);
+ }
+
+ private static class MoveTransaction extends QueueEntryTransaction
+ {
+ private final Queue _destinationQueue;
+
+ public MoveTransaction(Queue sourceQueue, List<Long> messageIds, Queue destinationQueue)
+ {
+ super(sourceQueue, messageIds);
+ _destinationQueue = destinationQueue;
+ }
+
+ protected void updateEntry(QueueEntry entry, VirtualHost.Transaction txn)
+ {
+ txn.move(entry, _destinationQueue);
+ }
+ }
+
+ private static class CopyTransaction extends QueueEntryTransaction
+ {
+ private final Queue _destinationQueue;
+
+ public CopyTransaction(Queue sourceQueue, List<Long> messageIds, Queue destinationQueue)
+ {
+ super(sourceQueue, messageIds);
+ _destinationQueue = destinationQueue;
+ }
+
+ protected void updateEntry(QueueEntry entry, VirtualHost.Transaction txn)
+ {
+ txn.copy(entry, _destinationQueue);
+ }
+ }
+
+ private static class DeleteTransaction extends QueueEntryTransaction
+ {
+ public DeleteTransaction(Queue sourceQueue, List<Long> messageIds)
+ {
+ super(sourceQueue, messageIds);
+ }
+
+ protected void updateEntry(QueueEntry entry, VirtualHost.Transaction txn)
+ {
+ txn.dequeue(entry);
+ }
+ }
+
+
+
+ private class MessageCollector implements QueueEntryVisitor
+ {
+ private final int _first;
+ private final int _last;
+ private int _position = -1;
+ private final List<Map<String, Object>> _messages = new ArrayList<Map<String, Object>>();
+
+ private MessageCollector(int first, int last)
+ {
+ _first = first;
+ _last = last;
+ }
+
+
+ public boolean visit(QueueEntry entry)
+ {
+
+ _position++;
+ if((_first == -1 || _position >= _first) && (_last == -1 || _position <= _last))
+ {
+ final Map<String, Object> messageObject = convertToObject(entry, false);
+ messageObject.put("position", _position);
+ _messages.add(messageObject);
+ }
+ return _last != -1 && _position > _last;
+ }
+
+ public List<Map<String, Object>> getMessages()
+ {
+ return _messages;
+ }
+ }
+
+
+ private class MessageFinder implements QueueEntryVisitor
+ {
+ private final long _messageNumber;
+ private Map<String, Object> _messageObject;
+
+ private MessageFinder(long messageNumber)
+ {
+ _messageNumber = messageNumber;
+ }
+
+
+ public boolean visit(QueueEntry entry)
+ {
+ ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ if(_messageNumber == message.getMessageNumber())
+ {
+ MessageReference reference = message.newReference();
+ _messageObject = convertToObject(entry, true);
+ reference.release();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Map<String, Object> getMessageObject()
+ {
+ return _messageObject;
+ }
+ }
+
+ private Map<String, Object> convertToObject(QueueEntry entry, boolean includeContent)
+ {
+ Map<String, Object> object = new LinkedHashMap<String, Object>();
+ object.put("size", entry.getSize());
+ object.put("deliveryCount", entry.getDeliveryCount());
+ object.put("state",entry.isAvailable()
+ ? "Available"
+ : entry.isAcquired()
+ ? "Acquired"
+ : "");
+ final Subscription deliveredSubscription = entry.getDeliveredSubscription();
+ object.put("deliveredTo", deliveredSubscription == null ? null : deliveredSubscription.getSubscriptionID());
+ ServerMessage message = entry.getMessage();
+
+ if(message != null)
+ {
+ convertMessageProperties(object, message);
+ if(includeContent)
+ {
+ convertMessageHeaders(object, message);
+ }
+ }
+
+ return object;
+ }
+
+ private void convertMessageProperties(Map<String, Object> object, ServerMessage message)
+ {
+ object.put("id", message.getMessageNumber());
+ object.put("arrivalTime",message.getArrivalTime());
+ object.put("persistent", message.isPersistent());
+
+ final AMQMessageHeader messageHeader = message.getMessageHeader();
+ if(messageHeader != null)
+ {
+ addIfPresent(object, "messageId", messageHeader.getMessageId());
+ addIfPresent(object, "expirationTime", messageHeader.getExpiration());
+ addIfPresent(object, "applicationId", messageHeader.getAppId());
+ addIfPresent(object, "correlationId", messageHeader.getCorrelationId());
+ addIfPresent(object, "encoding", messageHeader.getEncoding());
+ addIfPresent(object, "mimeType", messageHeader.getMimeType());
+ addIfPresent(object, "priority", messageHeader.getPriority());
+ addIfPresent(object, "replyTo", messageHeader.getReplyTo());
+ addIfPresent(object, "timestamp", messageHeader.getTimestamp());
+ addIfPresent(object, "type", messageHeader.getType());
+ addIfPresent(object, "userId", messageHeader.getUserId());
+ }
+
+ }
+
+ private void addIfPresent(Map<String, Object> object, String name, Object property)
+ {
+ if(property != null)
+ {
+ object.put(name, property);
+ }
+ }
+
+ private void convertMessageHeaders(Map<String, Object> object, ServerMessage message)
+ {
+ final AMQMessageHeader messageHeader = message.getMessageHeader();
+ if(messageHeader != null)
+ {
+ Map<String, Object> headers = new HashMap<String,Object>();
+ for(String headerName : messageHeader.getHeaderNames())
+ {
+ headers.put(headerName, messageHeader.getHeader(headerName));
+ }
+ object.put("headers", headers);
+ }
+ }
+
+ /*
+ * POST moves or copies messages to the given queue from a queue specified in the posted JSON data
+ */
+ @Override
+ protected void onPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+
+ try
+ {
+ final Queue sourceQueue = getQueueFromRequest(request);
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ @SuppressWarnings("unchecked")
+ Map<String,Object> providedObject = mapper.readValue(request.getInputStream(), LinkedHashMap.class);
+
+ String destQueueName = (String) providedObject.get("destinationQueue");
+ Boolean move = (Boolean) providedObject.get("move");
+
+ final VirtualHost vhost = sourceQueue.getParent(VirtualHost.class);
+
+ boolean isMoveTransaction = move != null && Boolean.valueOf(move);
+
+ // FIXME: added temporary authorization check until we introduce management layer
+ // and review current ACL rules to have common rules for all management interfaces
+ String methodName = isMoveTransaction? "moveMessages":"copyMessages";
+ if (isQueueUpdateMethodAuthorized(methodName, vhost.getName()))
+ {
+ final Queue destinationQueue = getQueueFromVirtualHost(destQueueName, vhost);
+ final List messageIds = new ArrayList((List) providedObject.get("messages"));
+ QueueEntryTransaction txn =
+ isMoveTransaction
+ ? new MoveTransaction(sourceQueue, messageIds, destinationQueue)
+ : new CopyTransaction(sourceQueue, messageIds, destinationQueue);
+ vhost.executeTransaction(txn);
+ response.setStatus(HttpServletResponse.SC_OK);
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+ catch(RuntimeException e)
+ {
+ LOGGER.error("Failure to perform message opertion", e);
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ /*
+ * DELETE removes messages from the queue
+ */
+ @Override
+ protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+
+ final Queue sourceQueue = getQueueFromRequest(request);
+
+ final VirtualHost vhost = sourceQueue.getParent(VirtualHost.class);
+
+
+ final List<Long> messageIds = new ArrayList<Long>();
+ for(String idStr : request.getParameterValues("id"))
+ {
+ messageIds.add(Long.valueOf(idStr));
+ }
+
+ // FIXME: added temporary authorization check until we introduce management layer
+ // and review current ACL rules to have common rules for all management interfaces
+ if (isQueueUpdateMethodAuthorized("deleteMessages", vhost.getName()))
+ {
+ vhost.executeTransaction(new DeleteTransaction(sourceQueue, messageIds));
+ response.setStatus(HttpServletResponse.SC_OK);
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+
+ }
+
+ private boolean isQueueUpdateMethodAuthorized(String methodName, String virtualHost)
+ {
+ SecurityManager securityManager = getSecurityManager(virtualHost);
+ return securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName);
+ }
+
+ private SecurityManager getSecurityManager(String virtualHost)
+ {
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ SecurityManager security;
+ if (virtualHost == null)
+ {
+ security = appRegistry.getSecurityManager();
+ }
+ else
+ {
+ security = appRegistry.getVirtualHostRegistry().getVirtualHost(virtualHost).getSecurityManager();
+ }
+ return security;
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
new file mode 100644
index 0000000000..593377beed
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
@@ -0,0 +1,586 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.SocketAddress;
+import java.util.*;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQSecurityException;
+import org.apache.qpid.server.model.*;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+
+public class RestServlet extends AbstractServlet
+{
+ private static final Logger LOGGER = Logger.getLogger(RestServlet.class);
+ /**
+ * An initialization parameter to specify hierarchy
+ */
+ private static final String HIERARCHY_INIT_PARAMETER = "hierarchy";
+
+ public static final String DEPTH_PARAM = "depth";
+ public static final String SORT_PARAM = "sort";
+
+ public static final Set<String> RESERVED_PARAMS = new HashSet<String>(Arrays.asList(DEPTH_PARAM, SORT_PARAM));
+
+ private Class<? extends ConfiguredObject>[] _hierarchy;
+
+ private volatile boolean initializationRequired = false;
+
+ public RestServlet()
+ {
+ super();
+ initializationRequired = true;
+ }
+
+ public RestServlet(Broker broker, Class<? extends ConfiguredObject>... hierarchy)
+ {
+ super(broker);
+ _hierarchy = hierarchy;
+ }
+
+ @Override
+ public void init() throws ServletException
+ {
+ if (initializationRequired)
+ {
+ doInitialization();
+ initializationRequired = false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void doInitialization() throws ServletException
+ {
+ ServletConfig config = getServletConfig();
+ String hierarchy = config.getInitParameter(HIERARCHY_INIT_PARAMETER);
+ if (hierarchy != null && !"".equals(hierarchy))
+ {
+ List<Class<? extends ConfiguredObject>> classes = new ArrayList<Class<? extends ConfiguredObject>>();
+ String[] hierarchyItems = hierarchy.split(",");
+ for (String item : hierarchyItems)
+ {
+ Class<?> itemClass = null;
+ try
+ {
+ itemClass = Class.forName(item);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ itemClass = Class.forName("org.apache.qpid.server.model." + item);
+ }
+ catch (ClassNotFoundException e1)
+ {
+ throw new ServletException("Unknown configured object class '" + item
+ + "' is specified in hierarchy for " + config.getServletName());
+ }
+ }
+ Class<? extends ConfiguredObject> clazz = (Class<? extends ConfiguredObject>)itemClass;
+ classes.add(clazz);
+ }
+ Class<? extends ConfiguredObject>[] hierachyClasses = (Class<? extends ConfiguredObject>[])new Class[classes.size()];
+ _hierarchy = classes.toArray(hierachyClasses);
+ }
+ else
+ {
+ _hierarchy = (Class<? extends ConfiguredObject>[])new Class[0];
+ }
+ }
+
+ protected Collection<ConfiguredObject> getObjects(HttpServletRequest request)
+ {
+ List<String> names = new ArrayList<String>();
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ names.addAll(Arrays.asList(path.split("/")));
+
+ if(names.size() > _hierarchy.length)
+ {
+ throw new IllegalArgumentException("Too many entries in path");
+ }
+ }
+
+ Collection<ConfiguredObject> parents = Collections.singleton((ConfiguredObject) getBroker());
+ Collection<ConfiguredObject> children = new ArrayList<ConfiguredObject>();
+
+ Map<Class<? extends ConfiguredObject>, String> filters =
+ new HashMap<Class<? extends ConfiguredObject>, String>();
+
+ for(int i = 0; i < _hierarchy.length; i++)
+ {
+ if(i == 0 || Model.getChildTypes(_hierarchy[i - 1]).contains(_hierarchy[i]))
+ {
+
+ for(ConfiguredObject parent : parents)
+ {
+ if(names.size() > i
+ && names.get(i) != null
+ && !names.get(i).equals("*")
+ && names.get(i).trim().length() != 0)
+ {
+ for(ConfiguredObject child : parent.getChildren(_hierarchy[i]))
+ {
+ if(child.getName().equals(names.get(i)))
+ {
+ children.add(child);
+ }
+ }
+ }
+ else
+ {
+ children.addAll(parent.getChildren(_hierarchy[i]));
+ }
+ }
+ }
+ else
+ {
+ children = parents;
+ if(names.size() > i
+ && names.get(i) != null
+ && !names.get(i).equals("*")
+ && names.get(i).trim().length() != 0)
+ {
+ filters.put(_hierarchy[i], names.get(i));
+ }
+ }
+
+ parents = children;
+ children = new ArrayList<ConfiguredObject>();
+ }
+
+ if(!filters.isEmpty())
+ {
+ Collection<ConfiguredObject> potentials = parents;
+ parents = new ArrayList<ConfiguredObject>();
+
+ for(ConfiguredObject o : potentials)
+ {
+
+ boolean match = true;
+
+ for(Map.Entry<Class<? extends ConfiguredObject>, String> entry : filters.entrySet())
+ {
+ Collection<? extends ConfiguredObject> ancestors =
+ getAncestors(getConfiguredClass(),entry.getKey(), o);
+ match = false;
+ for(ConfiguredObject ancestor : ancestors)
+ {
+ if(ancestor.getName().equals(entry.getValue()))
+ {
+ match = true;
+ break;
+ }
+ }
+ if(!match)
+ {
+ break;
+ }
+ }
+ if(match)
+ {
+ parents.add(o);
+ }
+
+ }
+ }
+
+ return filter(parents, request);
+ }
+
+ private Collection<ConfiguredObject> filter(Collection<ConfiguredObject> objects, HttpServletRequest request)
+ {
+
+
+ Map<String, Collection<String>> filters = new HashMap<String, Collection<String>>();
+
+ for(String param : (Collection<String>) Collections.list(request.getParameterNames()))
+ {
+ if(!RESERVED_PARAMS.contains(param))
+ {
+ filters.put(param, Arrays.asList(request.getParameterValues(param)));
+ }
+ }
+
+ if(filters.isEmpty())
+ {
+ return objects;
+ }
+
+ Collection<ConfiguredObject> filteredObj = new ArrayList<ConfiguredObject>(objects);
+
+ Iterator<ConfiguredObject> iter = filteredObj.iterator();
+
+ while(iter.hasNext())
+ {
+ ConfiguredObject obj = iter.next();
+ for(Map.Entry<String, Collection<String>> entry : filters.entrySet())
+ {
+ Object value = obj.getAttribute(entry.getKey());
+ if(!entry.getValue().contains(String.valueOf(value)))
+ {
+ iter.remove();
+ }
+ }
+
+ }
+
+ return filteredObj;
+ }
+
+ private Collection<? extends ConfiguredObject> getAncestors(Class<? extends ConfiguredObject> childType,
+ Class<? extends ConfiguredObject> ancestorType,
+ ConfiguredObject child)
+ {
+ Collection<ConfiguredObject> ancestors = new HashSet<ConfiguredObject>();
+ Collection<Class<? extends ConfiguredObject>> parentTypes = Model.getParentTypes(childType);
+
+ for(Class<? extends ConfiguredObject> parentClazz : parentTypes)
+ {
+ if(parentClazz == ancestorType)
+ {
+ ConfiguredObject parent = child.getParent(parentClazz);
+ if(parent != null)
+ {
+ ancestors.add(parent);
+ }
+ }
+ else
+ {
+ ConfiguredObject parent = child.getParent(parentClazz);
+ if(parent != null)
+ {
+ ancestors.addAll(getAncestors(parentClazz, ancestorType, parent));
+ }
+ }
+ }
+
+ return ancestors;
+ }
+
+
+ protected Map<String, Object> convertObjectToMap(final ConfiguredObject confObject,
+ Class<? extends ConfiguredObject> clazz,
+ int depth)
+ {
+ Map<String, Object> object = new LinkedHashMap<String, Object>();
+
+ for(String name : confObject.getAttributeNames())
+ {
+ Object value = confObject.getAttribute(name);
+ if(value instanceof ConfiguredObject)
+ {
+ object.put(name, ((ConfiguredObject) value).getName());
+ }
+ else if(value != null)
+ {
+ object.put(name, value);
+ }
+ }
+
+ Statistics statistics = confObject.getStatistics();
+ Map<String, Object> statMap = new HashMap<String, Object>();
+ for(String name : statistics.getStatisticNames())
+ {
+ Object value = statistics.getStatistic(name);
+ if(value != null)
+ {
+ statMap.put(name, value);
+ }
+ }
+
+ if(!statMap.isEmpty())
+ {
+ object.put("statistics", statMap);
+ }
+
+ if(depth > 0)
+ {
+ for(Class<? extends ConfiguredObject> childClass : Model.getChildTypes(clazz))
+ {
+ Collection<? extends ConfiguredObject> children = confObject.getChildren(childClass);
+ if(children != null)
+ {
+ List<Map<String, Object>> childObjects = new ArrayList<Map<String, Object>>();
+
+ for(ConfiguredObject child : children)
+ {
+ childObjects.add(convertObjectToMap(child, childClass, depth-1));
+ }
+
+ if(!childObjects.isEmpty())
+ {
+ object.put(childClass.getSimpleName().toLowerCase()+"s",childObjects);
+ }
+ }
+ }
+ }
+ return object;
+ }
+
+ @Override
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ Collection<ConfiguredObject> allObjects = getObjects(request);
+
+ @SuppressWarnings("unchecked")
+ Map params = new HashMap(request.getParameterMap());
+
+ int depth = 1;
+ try
+ {
+ depth = Integer.parseInt(String.valueOf(params.remove("depth")));
+ }
+ catch (NumberFormatException e)
+ {
+ // Ignore
+ }
+
+ List<Map<String, Object>> output = new ArrayList<Map<String, Object>>();
+
+ // TODO - depth and sort special params, everything else should act as a filter
+ if(request.getParameter(DEPTH_PARAM)!=null)
+ {
+ try
+ {
+ depth = Integer.parseInt(request.getParameter(DEPTH_PARAM));
+ }
+ catch (NumberFormatException e)
+ {
+
+ }
+ }
+
+ for(ConfiguredObject configuredObject : allObjects)
+ {
+ output.add(convertObjectToMap(configuredObject, getConfiguredClass(),depth));
+ }
+
+ final PrintWriter writer = response.getWriter();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, output);
+
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ }
+
+ private Class<? extends ConfiguredObject> getConfiguredClass()
+ {
+ return _hierarchy.length == 0 ? Broker.class : _hierarchy[_hierarchy.length-1];
+ }
+
+ @Override
+ protected void onPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("application/json");
+
+ ObjectMapper mapper = new ObjectMapper();
+ @SuppressWarnings("unchecked")
+ Map<String,Object> providedObject = mapper.readValue(request.getInputStream(), LinkedHashMap.class);
+
+
+ List<String> names = new ArrayList<String>();
+ if(request.getPathInfo() != null && request.getPathInfo().length()>0)
+ {
+ String path = request.getPathInfo().substring(1);
+ names.addAll(Arrays.asList(path.split("/")));
+
+ if(names.size() != _hierarchy.length)
+ {
+ throw new IllegalArgumentException("Path to object to create must be fully specified");
+ }
+ }
+
+
+ providedObject.put("name", names.get(names.size()-1));
+
+ @SuppressWarnings("unchecked")
+ Collection<ConfiguredObject>[] objects = new Collection[_hierarchy.length];
+ if(_hierarchy.length == 1)
+ {
+ try
+ {
+ getBroker().createChild(_hierarchy[0], providedObject);
+ }
+ catch (RuntimeException e)
+ {
+ setResponseStatus(response, e);
+ return;
+ }
+
+ }
+ else
+ {
+ for(int i = 0; i < _hierarchy.length-1; i++)
+ {
+ objects[i] = new HashSet<ConfiguredObject>();
+ if(i == 0)
+ {
+ for(ConfiguredObject object : getBroker().getChildren(_hierarchy[0]))
+ {
+ if(object.getName().equals(names.get(0)))
+ {
+ objects[0].add(object);
+ break;
+ }
+ }
+ }
+ else
+ {
+ for(int j = i-1; j >=0; j--)
+ {
+ if(Model.getChildTypes(_hierarchy[j]).contains(_hierarchy[i]))
+ {
+ for(ConfiguredObject parent : objects[j])
+ {
+ for(ConfiguredObject object : parent.getChildren(_hierarchy[i]))
+ {
+ if(object.getName().equals(names.get(i)))
+ {
+ objects[i].add(object);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ }
+ List<ConfiguredObject> parents = new ArrayList<ConfiguredObject>();
+ Class<? extends ConfiguredObject> objClass = getConfiguredClass();
+ Collection<Class<? extends ConfiguredObject>> parentClasses = Model.getParentTypes(objClass);
+ for(int i = _hierarchy.length-2; i >=0 ; i--)
+ {
+ if(parentClasses.contains(_hierarchy[i]))
+ {
+ if(objects[i].size() == 1)
+ {
+ parents.add(objects[i].iterator().next());
+ }
+ else
+ {
+ throw new IllegalArgumentException("Cannot deduce parent of class "
+ + _hierarchy[i].getSimpleName());
+ }
+ }
+
+ }
+ ConfiguredObject theParent = parents.remove(0);
+ ConfiguredObject[] otherParents = parents.toArray(new ConfiguredObject[parents.size()]);
+
+ try
+ {
+
+ Collection<? extends ConfiguredObject> existingChildren = theParent.getChildren(objClass);
+ for(ConfiguredObject obj: existingChildren)
+ {
+ if((providedObject.containsKey("id") && String.valueOf(providedObject.get("id")).equals(obj.getId().toString()))
+ || (obj.getName().equals(providedObject.get("name")) && equalParents(obj, otherParents)))
+ {
+ doUpdate(obj, providedObject);
+ }
+ }
+ theParent.createChild(objClass, providedObject, otherParents);
+ }
+ catch (RuntimeException e)
+ {
+ setResponseStatus(response, e);
+ return;
+ }
+
+ }
+ response.setStatus(HttpServletResponse.SC_CREATED);
+ }
+
+ private void doUpdate(ConfiguredObject obj, Map<String, Object> providedObject)
+ {
+ for(Map.Entry<String,Object> entry : providedObject.entrySet())
+ {
+ obj.setAttribute(entry.getKey(), obj.getAttribute(entry.getKey()), entry.getValue());
+ }
+ //TODO - Implement.
+ }
+
+ private boolean equalParents(ConfiguredObject obj, ConfiguredObject[] otherParents)
+ {
+ if(otherParents == null || otherParents.length == 0)
+ {
+ return true;
+ }
+ return false; //TODO - Implement.
+ }
+
+ private void setResponseStatus(HttpServletResponse response, RuntimeException e) throws IOException
+ {
+ if (e.getCause() instanceof AMQSecurityException)
+ {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ else
+ {
+ LOGGER.warn("Unexpected exception is caught", e);
+
+ // TODO
+ response.setStatus(HttpServletResponse.SC_CONFLICT);
+ }
+ }
+
+ @Override
+ protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+ try
+ {
+ Collection<ConfiguredObject> allObjects = getObjects(request);
+ for(ConfiguredObject o : allObjects)
+ {
+ o.setDesiredState(o.getActualState(), State.DELETED);
+ }
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ }
+ catch(RuntimeException e)
+ {
+ setResponseStatus(response, e);
+ }
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java
new file mode 100644
index 0000000000..1b78611a50
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java
@@ -0,0 +1,256 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+import javax.security.auth.Subject;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+
+public class SaslServlet extends AbstractServlet
+{
+ private static final Logger LOGGER = Logger.getLogger(SaslServlet.class);
+
+ private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+ private static final String ATTR_RANDOM = "SaslServlet.Random";
+ 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;
+
+
+ public SaslServlet()
+ {
+ super();
+ }
+
+ public SaslServlet(Broker broker)
+ {
+ super(broker);
+ }
+
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws
+ ServletException,
+ IOException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ HttpSession session = request.getSession();
+ Random rand = getRandom(session);
+
+ AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request));
+ String[] mechanisms = authManager.getMechanisms().split(" ");
+ Map<String, Object> outputObject = new LinkedHashMap<String, Object>();
+ final Subject subject = (Subject) session.getAttribute("subject");
+ if(subject != null)
+ {
+ final Principal principal = subject.getPrincipals().iterator().next();
+ outputObject.put("user", principal.getName());
+ }
+ else if (request.getRemoteUser() != null)
+ {
+ outputObject.put("user", request.getRemoteUser());
+ }
+
+ outputObject.put("mechanisms", (Object) mechanisms);
+
+ final PrintWriter writer = response.getWriter();
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, outputObject);
+
+ }
+
+ private Random getRandom(final HttpSession session)
+ {
+ Random rand = (Random) session.getAttribute(ATTR_RANDOM);
+ if(rand == null)
+ {
+ synchronized (SECURE_RANDOM)
+ {
+ rand = new Random(SECURE_RANDOM.nextLong());
+ }
+ session.setAttribute(ATTR_RANDOM, rand);
+ }
+ return rand;
+ }
+
+
+ @Override
+ protected void onPost(final HttpServletRequest request, final HttpServletResponse response)
+ throws ServletException, IOException
+ {
+ try
+ {
+ response.setContentType("application/json");
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader("Expires", 0);
+
+ HttpSession session = request.getSession();
+
+ String mechanism = request.getParameter("mechanism");
+ String id = request.getParameter("id");
+ String saslResponse = request.getParameter("response");
+
+ AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request));
+
+ if(mechanism != null)
+ {
+ if(id == null)
+ {
+ SaslServer saslServer = authManager.createSaslServer(mechanism, request.getServerName(), null/*TODO*/);
+ evaluateSaslResponse(response, session, saslResponse, saslServer);
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
+ session.removeAttribute(ATTR_ID);
+ session.removeAttribute(ATTR_SASL_SERVER);
+ session.removeAttribute(ATTR_EXPIRY);
+
+ }
+
+ }
+ else
+ {
+ if(id != null)
+ {
+ if(id.equals(session.getAttribute(ATTR_ID)) && System.currentTimeMillis() < (Long) session.getAttribute(ATTR_EXPIRY))
+ {
+ SaslServer saslServer = (SaslServer) session.getAttribute(ATTR_SASL_SERVER);
+ evaluateSaslResponse(response, session, saslResponse, saslServer);
+
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
+ session.removeAttribute(ATTR_ID);
+ session.removeAttribute(ATTR_SASL_SERVER);
+ session.removeAttribute(ATTR_EXPIRY);
+ }
+ }
+ else
+ {
+ response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
+ session.removeAttribute(ATTR_ID);
+ session.removeAttribute(ATTR_SASL_SERVER);
+ session.removeAttribute(ATTR_EXPIRY);
+
+ }
+ }
+ }
+ catch(IOException e)
+ {
+ LOGGER.error("Error processing SASL request", e);
+ throw e;
+ }
+ catch(RuntimeException e)
+ {
+ LOGGER.error("Error processing SASL request", e);
+ throw e;
+ }
+
+ }
+
+ private void evaluateSaslResponse(final HttpServletResponse response,
+ final HttpSession session,
+ final String saslResponse, final SaslServer saslServer) throws IOException
+ {
+ final String id;
+ byte[] challenge;
+ try
+ {
+ challenge = saslServer.evaluateResponse(saslResponse == null ? new byte[0] : Base64.decodeBase64(saslResponse.getBytes()));
+ }
+ catch(SaslException e)
+ {
+
+ session.removeAttribute(ATTR_ID);
+ session.removeAttribute(ATTR_SASL_SERVER);
+ session.removeAttribute(ATTR_EXPIRY);
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+ return;
+ }
+
+ if(saslServer.isComplete())
+ {
+ final Subject subject = new Subject();
+ subject.getPrincipals().add(new UsernamePrincipal(saslServer.getAuthorizationID()));
+ session.setAttribute("subject", subject);
+ session.removeAttribute(ATTR_ID);
+ session.removeAttribute(ATTR_SASL_SERVER);
+ session.removeAttribute(ATTR_EXPIRY);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+
+ }
+ else
+ {
+ Random rand = getRandom(session);
+ id = String.valueOf(rand.nextLong());
+ session.setAttribute(ATTR_ID, id);
+ session.setAttribute(ATTR_SASL_SERVER, saslServer);
+ session.setAttribute(ATTR_EXPIRY, System.currentTimeMillis() + SASL_EXCHANGE_EXPIRY);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ Map<String, Object> outputObject = new LinkedHashMap<String, Object>();
+ outputObject.put("id", id);
+ 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);
+
+ }
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java
new file mode 100644
index 0000000000..e4ba374f89
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java
@@ -0,0 +1,103 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Model;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+public class StructureServlet extends AbstractServlet
+{
+ public StructureServlet()
+ {
+ super();
+ }
+
+ public StructureServlet(Broker broker)
+ {
+ super(broker);
+ }
+
+ @Override
+ protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ response.setContentType("application/json");
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ response.setHeader("Cache-Control","no-cache");
+ response.setHeader("Pragma","no-cache");
+ response.setDateHeader ("Expires", 0);
+
+ Map<String,Object> structure = generateStructure(getBroker(), Broker.class);
+
+ final PrintWriter writer = response.getWriter();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+ mapper.writeValue(writer, structure);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ }
+
+ private Map<String, Object> generateStructure(ConfiguredObject object, Class<? extends ConfiguredObject> clazz)
+ {
+ Map<String, Object> structure = new LinkedHashMap<String, Object>();
+ structure.put("id", object.getId());
+ structure.put("name", object.getName());
+
+ for(Class<? extends ConfiguredObject> childClass : Model.getChildTypes(clazz))
+ {
+ Collection<? extends ConfiguredObject> children = object.getChildren(childClass);
+ if(children != null)
+ {
+ List<Map<String, Object>> childObjects = new ArrayList<Map<String, Object>>();
+
+ for(ConfiguredObject child : children)
+ {
+ childObjects.add(generateStructure(child, childClass));
+ }
+
+ if(!childObjects.isEmpty())
+ {
+ structure.put(pluralize(childClass),childObjects);
+ }
+ }
+ }
+
+ return structure;
+ }
+
+ private String pluralize(Class<? extends ConfiguredObject> childClass)
+ {
+ String name = childClass.getSimpleName().toLowerCase();
+ return name + (name.endsWith("s") ? "es" : "s");
+ }
+}
diff --git a/java/broker-plugins/management-http/src/main/java/resources/addBinding.html b/java/broker-plugins/management-http/src/main/java/resources/addBinding.html
new file mode 100644
index 0000000000..8dbd219c8d
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/addBinding.html
@@ -0,0 +1,42 @@
+<!--
+ ~ 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.
+ -->
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Binding'" id="addBinding">
+ <form id="formAddBinding" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Exchange Name*: </strong></td>
+ <td><div id="addBinding.selectExchangeDiv"></div></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Queue Name*: </strong></td>
+ <td><div id="addBinding.selectQueueDiv"></div></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Binding Key*: </strong></td>
+ <td><input type="text" required="true" name="name" id="formAddbinding.bindingKey" placeholder="Binding Key"
+ dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td>
+ </tr>
+ </table>
+ <br/>
+
+ <!-- submit buttons -->
+ <input type="submit" value="Create Binding" label="Create Binding" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/addExchange.html b/java/broker-plugins/management-http/src/main/java/resources/addExchange.html
new file mode 100644
index 0000000000..10ac5388ff
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/addExchange.html
@@ -0,0 +1,54 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Exchange'" id="addExchange">
+ <form id="formAddExchange" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Exchange Name*: </strong></td>
+ <td><input type="text" required="true" name="name" id="formAddExchange.name" placeholder="Exchange Name"
+ dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied"
+ data-dojo-props="regExp:'^(?!qpid\.|amq\.|\<\<default\>\>).*$', invalidMessage:'Reserved exchange name!'"/></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Durable? </strong></td>
+ <td><input type="checkbox" name="durable" id="formAddExchange.durable" value="durable" checked="checked" dojoType="dijit.form.CheckBox" /></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Exchange Type: </strong></td>
+ <td>
+ <select name="type" id="formAddExchange.type" dojoType="dijit.form.FilteringSelect">
+ <option value="direct">direct</option>
+ <option value="topic">topic</option>
+ <option value="headers">headers</option>
+ <option value="fanout">fanout</option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+ <!-- submit buttons -->
+ <input type="submit" value="Create Exchange" label="Create Exchange" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/addQueue.html b/java/broker-plugins/management-http/src/main/java/resources/addQueue.html
new file mode 100644
index 0000000000..d396f28877
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/addQueue.html
@@ -0,0 +1,182 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Queue'" id="addQueue">
+ <form id="formAddQueue" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Queue Name*: </strong></td>
+ <td><input type="text" required="true" name="name" id="formAddQueue.name" placeholder="Queue Name"
+ dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Durable? </strong></td>
+ <td><input type="checkbox" name="durable" id="formAddQueue.durable" value="durable" checked="checked" dojoType="dijit.form.CheckBox" /></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Queue Type: </strong></td>
+ <td>
+ <input type="radio" id="formAddQueueTypeStandard" name="type" value="standard" checked="checked" dojoType="dijit.form.RadioButton" />
+ <label for="formAddQueueTypeStandard">Standard</label>
+ &nbsp;&nbsp;
+ <input type="radio" id="formAddQueueTypePriority" name="type" value="priority" dojoType="dijit.form.RadioButton" />
+ <label for="formAddQueueTypePriority">Priority</label>
+ &nbsp;&nbsp;
+ <input type="radio" id="formAddQueueTypeLVQ" name="type" value="lvq" dojoType="dijit.form.RadioButton" />
+ <label for="formAddQueueTypeLVQ">LVQ</label>
+ &nbsp;&nbsp;
+ <input type="radio" id="formAddQueueTypeSorted" name="type" value="sorted" dojoType="dijit.form.RadioButton" />
+ <label for="formAddQueueTypeSorted">Sorted</label>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+ <div id="formAddQueueTypePriority:fields" style="display:none"
+ data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Priority Queue Settings'">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Priorities: </strong></td>
+ <td><input data-dojo-type="dijit.form.NumberSpinner" id="formAddQueue.priorities"
+ name="priorities" value="10" smallDelta="1" constraints="{min:1,max:10,places:0}"/>
+ </tr>
+ </table>
+ </div>
+
+
+ <div id="formAddQueueTypeLVQ:fields" style="display:none"
+ data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Last Value Queue Settings'">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>LVQ Message Property: </strong></td>
+ <td><input type="text" name="lvqKey" id="formAddQueue.lvqkey"
+ placeholder="qpid.LVQ_key" dojoType="dijit.form.ValidationTextBox" /></td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="formAddQueueTypeSorted:fields" style="display:none"
+ data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Sorted Queue Settings'">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Sort Message Property: </strong></td>
+ <td><input type="text" name="sortKey" id="formAddQueue.sortkey" required="false"
+ placeholder="" dojoType="dijit.form.ValidationTextBox" /></td>
+ </tr>
+ </table>
+ </div>
+
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Flow Control Settings', open: false">
+ <table cellpadding="0" cellspacing="2">
+
+ <!-- x-qpid-capacity -->
+ <tr>
+ <td valign="top"><strong>Capacity: </strong></td>
+ <td><input type="text" required="false" name="queueFlowControlSizeBytes" id="formAddQueue.capacity" placeholder="Size in bytes"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value"/></td>
+ </tr>
+ <!-- x-qpid-flow-resume-capacity -->
+ <tr>
+ <td valign="top"><strong>Resume Capacity: </strong></td>
+ <td><input type="text" required="false" name="queueFlowResumeSizeBytes" id="formAddQueue.flowResumeCapacity" placeholder="Size in bytes"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value"/></td>
+ </tr>
+ </table>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Alerting Settings', open: false">
+ <table cellpadding="0" cellspacing="2">
+ <!-- x-qpid-maximum-message-count -->
+ <tr>
+ <td valign="top"><strong>Queue Depth: </strong></td>
+ <td><input type="text" required="false" name="alertThresholdQueueDepthMessages" id="formAddQueue.maximumMessageCount" placeholder="Count of messages"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value" /></td>
+ </tr>
+ <!-- x-qpid-maximum-message-age -->
+ <tr>
+ <td valign="top"><strong>Message Age: </strong></td>
+ <td><input type="text" required="false" name="alertThresholdMessageAge" id="formAddQueue.maximumMessageAge" placeholder="Time in ms"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value" /></td>
+ </tr>
+ <!-- x-qpid-maximum-message-size -->
+ <tr>
+ <td valign="top"><strong>Message Size: </strong></td>
+ <td><input type="text" required="false" name="alertThresholdMessageSize" id="formAddQueue.maximumMessageSize" placeholder="Size in bytes"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value"/></td>
+ </tr>
+ <!-- x-qpid-minimum-alert-repeat-gap -->
+ <tr>
+ <td valign="top"><strong>Gap between alerts: </strong></td>
+ <td><input type="text" required="false" name="alertRepeatGap" id="formAddQueue.minimumAlertRepeatGap" placeholder="Time in ms"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value" /></td>
+ </tr>
+ </table>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Other Settings', open: false">
+ <table cellpadding="0" cellspacing="2">
+
+ <!-- x-qpid-maximum-delivery-count -->
+ <tr>
+ <td valign="top"><strong>Maximum Delivery Retries: </strong></td>
+ <td><input type="text" required="false" name="maximumDeliveryAttempts" id="formAddQueue.maximumDeliveryCount"
+ dojoType="dijit.form.ValidationTextBox"
+ trim="true"
+ regexp="[0-9]+"
+ invalidMessage= "Invalid value"/></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Create DLQ? </strong></td>
+ <td><input type="checkbox" name="dlqEnabled" id="formAddQueue.dlqEnabled" value="dlqEnabled" dojoType="dijit.form.CheckBox" /></td>
+ </tr>
+ </table>
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top">NOTE: Configuring maximum delivery retries on a queue which has no DLQ / AlternateExchange will result in messages being discarded after the limit is reached.</td>
+ </tr>
+ </table>
+ </div>
+ <br/>
+ <!-- submit buttons -->
+ <input type="submit" value="Create Queue" label="Create Queue" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/addUser.html b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/addUser.html
new file mode 100644
index 0000000000..785605f694
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/addUser.html
@@ -0,0 +1,42 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add User'" id="addUser">
+ <form id="formAddUser" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>User Name*: </strong></td>
+ <td><input type="text" required="true" name="name" id="formAddUser.name" placeholder="User Name"
+ dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Password*</strong></td>
+ <td><input type="password" required="true" name="password" id="formAddUser.password" dojoType="dijit.form.ValidationTextBox"/></td>
+ </tr>
+ </table>
+ <br/>
+
+ <!-- submit buttons -->
+ <input type="submit" value="Create User" label="Create User" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/setPassword.html b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/setPassword.html
new file mode 100644
index 0000000000..3d67463abd
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/setPassword.html
@@ -0,0 +1,42 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Set Password'" id="setPassword">
+ <form id="formSetPassword" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>User Name: </strong></td>
+ <td><input type="text" required="true" name="name" id="formSetPassword.name" placeholder="User Name"
+ dojoType="dijit.form.TextBox" enabled="false" /></td>
+ </tr>
+ <tr>
+ <td valign="top"><strong>Password*</strong></td>
+ <td><input type="password" required="true" name="password" id="formSetPassword.password" dojoType="dijit.form.ValidationTextBox"/></td>
+ </tr>
+ </table>
+ <br/>
+
+ <!-- submit buttons -->
+ <input type="submit" value="Set Password" label="Set Password" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/experimental/shutdown/build.xml b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html
index cb4254806b..baadc8c35f 100644
--- a/java/broker-plugins/experimental/shutdown/build.xml
+++ b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html
@@ -7,9 +7,9 @@
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- -
+ -
- http://www.apache.org/licenses/LICENSE-2.0
- -
+ -
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,15 +18,12 @@
- under the License.
-
-->
-<project name="AMQ Broker Shutdown Plugin" default="build">
-
- <property name="module.depends" value="common broker management/common broker-plugins"/>
- <property name="module.test.depends" value="test broker/test management/common client systests"/>
- <property name="module.manifest" value="MANIFEST.MF"/>
- <property name="module.plugin" value="true"/>
-
- <import file="../../../module.xml"/>
+<div class="PrincipalDatabaseAuthenticationManager">
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Users'">
+ <div class="users"></div>
+ <button data-dojo-type="dijit.form.Button" class="addUserButton">Add User</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteUserButton">Delete Users</button>
- <target name="bundle" depends="bundle-tasks"/>
+ </div>
-</project>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/css/common.css b/java/broker-plugins/management-http/src/main/java/resources/css/common.css
new file mode 100644
index 0000000000..78780edcd9
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/css/common.css
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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.
+ *
+ */
+* {
+ outline: none !important;
+}
+
+html, body {
+ height: 100%;
+ margin: 0;
+ margin-right: 40px;
+ padding: 0;
+ overflow: hidden;
+ font-family: Lucida Sans,Lucida Grande,Arial !important;
+ font-size: 13px !important;
+ background: white;
+ color: #333;
+}
+
+#pageLayout {
+ height: 100%;
+}
+button {
+ -webkit-transition: background-color 0.2s linear;
+ border-radius:4px;
+ -moz-border-radius: 4px 4px 4px 4px;
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
+ background-color: #E4F2FF;
+ background-image: url("../dojo/dijit/themes/claro/form/images/button.png");
+ background-position: center top;
+ background-repeat: repeat-x;
+ border: 1px solid #769DC0;
+ padding: 2px 8px 4px;
+ font-size:1em;
+}
+
+button:hover {
+ background-color: #AFD9FF;
+ color: #000000;
+}
+
+h1 {
+ font-size:1.5em;
+}
+
+.header {
+ height:100px;
+ background:url("../images/qpid-logo.png") left center no-repeat
+}
+
+.logo {
+ text-align:left;
+ vertical-align: top;
+ font-weight:600;
+ height: 90px;
+ padding-left: 200px;
+ padding-top: 1px;
+ padding-bottom: 10px;
+ font-size:14px;
+ font-family:"Verdana", cursive;
+}
+
+.footer {
+ color:#000000;
+ clear:both;
+ text-align:center;
+ font-size:11px;
+ line-height:17px;
+
+}
+
+div .messages {
+ width: 100%;
+ height: 350px;
+} \ No newline at end of file
diff --git a/java/broker-plugins/extras/build.xml b/java/broker-plugins/management-http/src/main/java/resources/footer.html
index 7c1d0be49f..fa84825e80 100644
--- a/java/broker-plugins/extras/build.xml
+++ b/java/broker-plugins/management-http/src/main/java/resources/footer.html
@@ -1,15 +1,15 @@
<!--
-
- Licensed to the Apache Software Foundation (ASF) under one
-nn - or more contributor license agreements. See the NOTICE file
- -n distributed with this work for additional information
+ - 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
@@ -18,14 +18,11 @@ nn - or more contributor license agreements. See the NOTICE file
- under the License.
-
-->
-<project name="Qpid Broker-Plugins Extras" default="build">
- <property name="module.depends" value="common client management/common broker broker-plugins"/>
- <property name="module.test.depends" value="test broker/test common/test"/>
- <property name="module.manifest" value="MANIFEST.MF"/>
- <property name="module.plugin" value="true"/>
-
- <import file="../../module.xml"/>
-
- <target name="bundle" depends="bundle-tasks" />
-</project>
+<div class="footer"><p>&#xA9; 2004-<span class="currentYear">2012</span> The Apache Software Foundation.
+ <br/>
+ Apache Qpid, Qpid, Apache, the Apache feather logo, and the Apache Qpid project logo are trademarks of
+ The Apache Software Foundation.
+ <br/>
+ All other marks mentioned may be trademarks or registered trademarks of their respective owners.
+</div> \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png b/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png
new file mode 100644
index 0000000000..95d49ea469
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png
Binary files differ
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js
new file mode 100644
index 0000000000..152504da86
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js
@@ -0,0 +1,213 @@
+/*
+ *
+ * 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.
+ *
+ */
+require(["dijit/form/DropDownButton", "dijit/TooltipDialog", "dijit/form/TextBox",
+ "dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5"]);
+var button;
+var usernameSpan;
+
+var encodeUTF8 = function encodeUTF8(str) {
+ var byteArray = [];
+ for (var i = 0; i < str.length; i++) {
+ if (str.charCodeAt(i) <= 0x7F) {
+ byteArray.push(str.charCodeAt(i));
+ }
+ else {
+ var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
+ for (var j = 0; j < h.length; j++)
+ byteArray.push(parseInt(h[j], 16));
+ }
+ }
+ return byteArray;
+};
+
+var decodeUTF8 = function decodeUTF8(byteArray)
+{
+ var str = '';
+ for (var i = 0; i < byteArray.length; i++)
+ str += byteArray[i] <= 0x7F?
+ byteArray[i] === 0x25 ? "%25" :
+ String.fromCharCode(byteArray[i]) :
+ "%" + byteArray[i].toString(16).toUpperCase();
+ return decodeURIComponent(str);
+};
+
+
+var saslPlain = function saslPlain(user, password)
+{
+ var responseArray = [ 0 ].concat(encodeUTF8( user )).concat( [ 0 ] ).concat( encodeUTF8( password ) );
+ var plainResponse = dojox.encoding.base64.encode(responseArray);
+
+ // Using dojo.xhrGet, as very little information is being sent
+ dojo.xhrPost({
+ // The URL of the request
+ url: "rest/sasl",
+ content: {
+ mechanism: "PLAIN",
+ response: plainResponse
+ },
+ handleAs: "json",
+ failOk: true
+ }).then(function()
+ {
+ updateAuthentication();
+ },
+ function(error)
+ {
+ if(error.status == 401)
+ {
+ alert("Authentication Failed");
+ }
+ else
+ {
+ alert(error);
+ }
+ updateAuthentication();
+ });
+};
+
+var saslCramMD5 = function saslCramMD5(user, password)
+{
+
+ // Using dojo.xhrGet, as very little information is being sent
+ dojo.xhrPost({
+ // The URL of the request
+ url: "rest/sasl",
+ content: {
+ mechanism: "CRAM-MD5"
+ },
+ handleAs: "json",
+ failOk: true
+ }).then(function(data)
+ {
+
+ var challengeBytes = dojox.encoding.base64.decode(data.challenge);
+ var wa=[];
+ var bitLength = challengeBytes.length*8;
+ for(var i=0; i<bitLength; i+=8)
+ {
+ wa[i>>5] |= (challengeBytes[i/8] & 0xFF)<<(i%32);
+ }
+ var challengeStr = dojox.encoding.digests.wordToString(wa).substring(0,challengeBytes.length);
+
+ var digest = user + " " + dojox.encoding.digests.MD5._hmac(challengeStr, password, dojox.encoding.digests.outputTypes.Hex);
+ var id = data.id;
+
+ var response = dojox.encoding.base64.encode(encodeUTF8( digest ));
+
+ dojo.xhrPost({
+ // The URL of the request
+ url: "rest/sasl",
+ content: {
+ id: id,
+ response: response
+ },
+ handleAs: "json",
+ failOk: true
+ }).then(function()
+ {
+ updateAuthentication();
+ },
+ function(error)
+ {
+ if(error.status == 401)
+ {
+ alert("Authentication Failed");
+ }
+ else
+ {
+ alert(error);
+ }
+ updateAuthentication();
+ });
+
+ },
+ function(error)
+ {
+ if(error.status == 401)
+ {
+ alert("Authentication Failed");
+ }
+ else
+ {
+ alert(error);
+ }
+ });
+};
+
+var doAuthenticate = function doAuthenticate()
+{
+ saslCramMD5(dojo.byId("username").value, dojo.byId("pass").value);
+ updateAuthentication();
+};
+
+
+var updateAuthentication = function updateAuthentication()
+{
+ dojo.xhrGet({
+ // The URL of the request
+ url: "rest/sasl",
+ handleAs: "json"
+ }).then(function(data)
+ {
+ if(data.user)
+ {
+ dojo.byId("authenticatedUser").innerHTML = data.user;
+ dojo.style(button.domNode, {visibility: 'hidden'});
+ dojo.style(usernameSpan, {visibility: 'visible'});
+ }
+ else
+ {
+ dojo.style(button.domNode, {visibility: 'visible'});
+ dojo.style(usernameSpan, {visibility: 'hidden'});
+ }
+ }
+ );
+};
+
+require(["dijit/form/DropDownButton", "dijit/TooltipDialog", "dijit/form/TextBox", "dojo/_base/xhr", "dojo/dom", "dojo/dom-construct", "dojo/domReady!"],
+ function(DropDownButton, TooltipDialog, TextBox, xhr, dom, domConstruct){
+ var dialog = new TooltipDialog({
+ content:
+ '<strong><label for="username" style="display:inline-block;width:100px;">Username:</label></strong>' +
+ '<div data-dojo-type="dijit.form.TextBox" id="username"></div><br/>' +
+ '<strong><label for="pass" style="display:inline-block;width:100px;">Password:</label></strong>' +
+ '<div data-dojo-type="dijit.form.TextBox" type="password" id="pass"></div><br/>' +
+ '<button data-dojo-type="dijit.form.Button" data-dojo-props="onClick:doAuthenticate" type="submit">Login</button>'
+ });
+
+ button = new DropDownButton({
+ label: "Login",
+ dropDown: dialog
+ });
+
+ usernameSpan = domConstruct.create("span", { innerHTML: '<strong>User: </strong><span id="authenticatedUser"></span>',
+ style: { visibility: "hidden" }});
+
+
+ var loginDiv = dom.byId("login");
+ loginDiv.appendChild(button.domNode);
+ loginDiv.appendChild(usernameSpan);
+
+
+
+
+ updateAuthentication();
+}); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js
new file mode 100644
index 0000000000..f7ede1a7f7
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js
@@ -0,0 +1,110 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/store/Memory",
+ "dojox/grid/DataGrid",
+ "dojo/data/ObjectStore",
+ "dojo/store/Observable"], function (Memory, DataGrid, ObjectStore, Observable) {
+
+ function UpdatableStore( data, divName, structure, func, props, Grid ) {
+
+ var that = this;
+ var GridType = DataGrid;
+
+ that.store = Observable(Memory({data: data, idProperty: "id"}));
+ that.dataStore = ObjectStore({objectStore: that.store});
+
+ var gridProperties = { store: that.dataStore,
+ structure: structure,
+ autoHeight: true
+ };
+ if(props) {
+ for(var prop in props) {
+ if(props.hasOwnProperty(prop))
+ {
+ gridProperties[ prop ] = props[ prop ];
+ }
+ }
+ }
+
+ if(Grid)
+ {
+ GridType = Grid;
+ }
+
+ that.grid = new GridType(gridProperties, divName);
+
+ // since we created this grid programmatically, call startup to render it
+ that.grid.startup();
+
+ if( func )
+ {
+ func(that);
+ }
+
+ }
+
+ UpdatableStore.prototype.update = function(data)
+ {
+
+ var store = this.store;
+ var theItem;
+
+ // handle deletes
+ // iterate over existing store... if not in new data then remove
+ store.query({ }).forEach(function(object) {
+ if(data) {
+ for(var i=0; i < data.length; i++) {
+ if(data[i].id == object.id) {
+ return;
+ }
+ }
+ }
+ store.remove(object.id);
+
+ });
+
+ // iterate over data...
+ if(data) {
+ for(var i=0; i < data.length; i++) {
+ if(theItem = store.get(data[i].id)) {
+ var modified;
+ for(var propName in data[i]) {
+ if(data[i].hasOwnProperty(propName)) {
+ if(theItem[ propName ] != data[i][ propName ]) {
+ theItem[ propName ] = data[i][ propName ];
+ modified = true;
+ }
+ }
+ }
+ if(modified) {
+ // ... check attributes for updates
+ store.notify(theItem, data[i].id);
+ }
+ } else {
+ // ,,, if not in the store then add
+ store.put(data[i]);
+ }
+ }
+ }
+
+ };
+ return UpdatableStore;
+});
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/footer.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/footer.js
new file mode 100644
index 0000000000..ea13b1fc53
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/footer.js
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+define(["dojo/_base/xhr", "dojo/query", "dojo/domReady!"], function (xhr, query) {
+ query('div[qpid-type="footer"]').forEach(function(node, index, arr) {
+ xhr.get({url: "footer.html",
+ sync: true,
+ load: function(data) {
+ node.innerHTML = data;
+ } });
+ });
+});
+
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js
new file mode 100644
index 0000000000..2f8683ee1c
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/formatter.js
@@ -0,0 +1,99 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+define(function () {
+ return {
+
+ formatBytes: function formatBytes(amount)
+ {
+ var returnVal = { units: "B",
+ value: "0"};
+
+
+ if(amount < 1000)
+ {
+ returnVal.value = amount.toPrecision(3);;
+ }
+ else if(amount < 1000 * 1024)
+ {
+ returnVal.units = "KB";
+ returnVal.value = (amount / 1024).toPrecision(3);
+ }
+ else if(amount < 1000 * 1024 * 1024)
+ {
+ returnVal.units = "MB";
+ returnVal.value = (amount / (1024 * 1024)).toPrecision(3);
+ }
+ else if(amount < 1000 * 1024 * 1024 * 1024)
+ {
+ returnVal.units = "GB";
+ returnVal.value = (amount / (1024 * 1024 * 1024)).toPrecision(3);
+ }
+
+ return returnVal;
+
+ },
+
+ formatTime: function formatTime(amount)
+ {
+ var returnVal = { units: "ms",
+ value: "0"};
+
+ if(amount < 1000)
+ {
+ returnVal.units = "ms";
+ returnVal.value = amount.toString();
+ }
+ else if(amount < 1000 * 60)
+ {
+ returnVal.units = "s";
+ returnVal.value = (amount / 1000).toPrecision(3);
+ }
+ else if(amount < 1000 * 60 * 60)
+ {
+ returnVal.units = "min";
+ returnVal.value = (amount / (1000 * 60)).toPrecision(3);
+ }
+ else if(amount < 1000 * 60 * 60 * 24)
+ {
+ returnVal.units = "hr";
+ returnVal.value = (amount / (1000 * 60 * 60)).toPrecision(3);
+ }
+ else if(amount < 1000 * 60 * 60 * 24 * 7)
+ {
+ returnVal.units = "d";
+ returnVal.value = (amount / (1000 * 60 * 60 * 24)).toPrecision(3);
+ }
+ else if(amount < 1000 * 60 * 60 * 24 * 365)
+ {
+ returnVal.units = "wk";
+ returnVal.value = (amount / (1000 * 60 * 60 * 24 * 7)).toPrecision(3);
+ }
+ else
+ {
+ returnVal.units = "yr";
+ returnVal.value = (amount / (1000 * 60 * 60 * 24 * 365)).toPrecision(3);
+ }
+
+ return returnVal;
+ }
+ };
+}); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/properties.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/properties.js
new file mode 100644
index 0000000000..8d85345b74
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/properties.js
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/has", "dojo/_base/sniff", "dojo/domReady!"],
+ function (has) {
+ var properties = {};
+ properties.useSyncGet = (has("ie") <= 8);
+ return properties;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/updater.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/updater.js
new file mode 100644
index 0000000000..86bbaa46ba
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/updater.js
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(function () {
+ var updateList = new Array();
+
+ setInterval(function() {
+ for(var i = 0; i < updateList.length; i++) {
+ var obj = updateList[i];
+ obj.update();
+ }
+ }, 5000); // TODO: Should make this configurable
+
+ return {
+ add: function(obj) {
+ updateList.push(obj);
+ },
+
+ remove: function(obj) {
+ for(var i = 0; i < updateList.length; i++) {
+ if(updateList[i] === obj) {
+ updateList.splice(i,1);
+ return;
+ }
+ }
+ }
+ };
+}); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
new file mode 100644
index 0000000000..08fdf5c99b
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr"],
+ function (xhr) {
+ var util = {};
+ if (Array.isArray) {
+ util.isArray = function (object) {
+ return Array.isArray(object);
+ };
+ } else {
+ util.isArray = function (object) {
+ return object instanceof Array;
+ };
+ }
+
+ util.flattenStatistics = function (data) {
+ var attrName, stats, propName, theList;
+ for(attrName in data) {
+ if(data.hasOwnProperty(attrName)) {
+ if(attrName == "statistics") {
+ stats = data.statistics;
+ for(propName in stats) {
+ if(stats.hasOwnProperty( propName )) {
+ data[ propName ] = stats[ propName ];
+ }
+ }
+ } else if(data[ attrName ] instanceof Array) {
+ theList = data[ attrName ];
+
+ for(var i=0; i < theList.length; i++) {
+ util.flattenStatistics( theList[i] );
+ }
+ }
+ }
+ }
+ };
+
+ util.isReservedExchangeName = function(exchangeName)
+ {
+ return exchangeName == null || exchangeName == "" || "<<default>>" == exchangeName || exchangeName.indexOf("amq.") == 0 || exchangeName.indexOf("qpid.") == 0;
+ };
+
+ util.deleteGridSelections = function(updater, gridName, url, confirmationMessageStart)
+ {
+ var grid = updater[gridName].grid;
+ var data = grid.selection.getSelected();
+ if(data.length)
+ {
+ var confirmationMessage = null;
+ if (data.length == 1)
+ {
+ confirmationMessage = confirmationMessageStart + " '" + data[0].name + "'?";
+ }
+ else
+ {
+ var names = '';
+ for(var i = 0; i<data.length; i++)
+ {
+ if (names)
+ {
+ names += ', ';
+ }
+ names += "\""+ data[i].name + "\"";
+ }
+ confirmationMessage = confirmationMessageStart + "s " + names + "?";
+ }
+ if(confirm(confirmationMessage))
+ {
+ var i, queryParam;
+ for(i = 0; i<data.length; i++)
+ {
+ if(queryParam)
+ {
+ queryParam += "&";
+ }
+ else
+ {
+ queryParam = "?";
+ }
+ queryParam += "id=" + data[i].id;
+ }
+ var query = url + queryParam;
+ var success = true
+ var failureReason = "";
+ xhr.del({url: query, sync: true, handleAs: "json"}).then(
+ function(data)
+ {
+ grid.setQuery({id: "*"});
+ grid.selection.deselectAll();
+ updater.update();
+ },
+ function(error) {success = false; failureReason = error;});
+ if(!success )
+ {
+ alert("Error:" + failureReason);
+ }
+ }
+ }
+ }
+
+ return util;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js
new file mode 100644
index 0000000000..7613fd5d71
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js
@@ -0,0 +1,122 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/_base/connect",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/UpdatableStore",
+ "dojox/grid/EnhancedGrid",
+ "dojox/grid/enhanced/plugins/Pagination",
+ "dojox/grid/enhanced/plugins/IndirectSelection",
+ "dojo/domReady!"],
+ function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid) {
+
+ function AuthenticationProvider(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "authenticationprovider", name: name };
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+ AuthenticationProvider.prototype.getTitle = function() {
+ return "AuthenticationProvider";
+ };
+
+ AuthenticationProvider.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showAuthProvider.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.authProviderAdapter = new AuthProviderUpdater(contentPane.containerNode, that.modelObj, that.controller);
+
+ updater.add( that.authProviderAdapter );
+
+ that.authProviderAdapter.update();
+
+ }});
+ };
+
+ AuthenticationProvider.prototype.close = function() {
+ updater.remove( this.authProviderAdapter );
+ };
+
+ function AuthProviderUpdater(node, authProviderObj, controller)
+ {
+ this.controller = controller;
+ this.name = query(".name", node)[0];
+ /*this.state = dom.byId("state");
+ this.durable = dom.byId("durable");
+ this.lifetimePolicy = dom.byId("lifetimePolicy");
+ */
+ this.query = "rest/authenticationprovider/"+encodeURIComponent(authProviderObj.name);
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data)
+ {
+ that.authProviderData = data[0];
+
+ util.flattenStatistics( that.authProviderData );
+
+ that.updateHeader();
+
+ require(["qpid/management/authenticationprovider/"+that.authProviderData.type],
+ function(SpecificProvider) {
+ that.details = new SpecificProvider(node, authProviderObj, controller);
+ that.details.update();
+ });
+
+ });
+
+ }
+
+ AuthProviderUpdater.prototype.updateHeader = function()
+ {
+ this.name.innerHTML = this.authProviderData[ "name" ];
+ /* this.state.innerHTML = this.brokerData[ "state" ];
+ this.durable.innerHTML = this.brokerData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.brokerData[ "lifetimePolicy" ];
+*/
+ };
+
+ AuthProviderUpdater.prototype.update = function()
+ {
+
+ var that = this;
+
+
+ };
+
+
+
+ return AuthenticationProvider;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js
new file mode 100644
index 0000000000..dcf6711073
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js
@@ -0,0 +1,205 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/_base/connect",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/UpdatableStore",
+ "dojox/grid/EnhancedGrid",
+ "dojox/grid/enhanced/plugins/Pagination",
+ "dojox/grid/enhanced/plugins/IndirectSelection",
+ "dojo/domReady!"],
+ function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid) {
+
+ function Broker(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "broker", name: name };
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+ Broker.prototype.getTitle = function()
+ {
+ return "Broker";
+ };
+
+ Broker.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showBroker.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.brokerUpdater = new BrokerUpdater(contentPane.containerNode, that.modelObj, that.controller);
+
+ updater.add( that.brokerUpdater );
+
+ that.brokerUpdater.update();
+
+ }});
+ };
+
+ Broker.prototype.close = function() {
+ updater.remove( this.brokerUpdater );
+ };
+
+ function BrokerUpdater(node, brokerObj, controller)
+ {
+ this.controller = controller;
+ this.name = query(".broker-name", node)[0];
+ /*this.state = dom.byId("state");
+ this.durable = dom.byId("durable");
+ this.lifetimePolicy = dom.byId("lifetimePolicy");
+ */
+ this.query = "rest/broker";
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data)
+ {
+ that.brokerData= data[0];
+
+ util.flattenStatistics( that.brokerData);
+
+ that.updateHeader();
+ that.vhostsGrid =
+ new UpdatableStore(that.brokerData.vhosts, query(".broker-virtualhosts")[0],
+ [ { name: "Virtual Host", field: "name", width: "120px"},
+ { name: "Connections", field: "connectionCount", width: "80px"},
+ { name: "Queues", field: "queueCount", width: "80px"},
+ { name: "Exchanges", field: "exchangeCount", width: "100%"}
+ ], function(obj) {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var name = obj.dataStore.getValue(theItem,"name");
+ that.controller.show("virtualhost", name, brokerObj);
+ });
+ });
+
+ that.portsGrid =
+ new UpdatableStore(that.brokerData.ports, query(".broker-ports")[0],
+ [ { name: "Address", field: "bindingAddress", width: "70px"},
+ { name: "Port", field: "port", width: "70px"},
+ { name: "Transports", field: "transports", width: "150px"},
+ { name: "Protocols", field: "protocols", width: "100%"}
+ ], function(obj) {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var name = obj.dataStore.getValue(theItem,"name");
+ that.controller.show("port", name, brokerObj);
+ });
+ });
+
+ });
+
+ xhr.get({url: "rest/logrecords", sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data)
+ {
+ that.logData = data;
+
+ var gridProperties = {
+ height: 400,
+ plugins: {
+ pagination: {
+ pageSizes: ["10", "25", "50", "100"],
+ description: true,
+ sizeSwitch: true,
+ pageStepper: true,
+ gotoButton: true,
+ maxPageStep: 4,
+ position: "bottom"
+ }
+ }};
+
+
+ that.logfileGrid =
+ new UpdatableStore(that.logData, query(".broker-logfile")[0],
+ [ { name: "Timestamp", field: "timestamp", width: "200px",
+ formatter: function(val) {
+ var d = new Date(0);
+ d.setUTCSeconds(val/1000);
+
+ return d.toLocaleString();
+ }},
+ { name: "Level", field: "level", width: "60px"},
+ { name: "Logger", field: "logger", width: "280px"},
+ { name: "Thread", field: "thread", width: "120px"},
+ { name: "Log Message", field: "message", width: "100%"}
+
+ ], null, gridProperties, EnhancedGrid);
+ });
+ }
+
+ BrokerUpdater.prototype.updateHeader = function()
+ {
+ this.name.innerHTML = this.brokerData[ "name" ];
+ /* this.state.innerHTML = this.brokerData[ "state" ];
+ this.durable.innerHTML = this.brokerData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.brokerData[ "lifetimePolicy" ];
+*/
+ };
+
+ BrokerUpdater.prototype.update = function()
+ {
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ that.brokerData = data[0];
+ util.flattenStatistics( that.brokerData );
+
+ that.updateHeader();
+
+ that.vhostsGrid.update(that.brokerData.virtualhosts);
+
+ that.portsGrid.update(that.brokerData.ports);
+
+
+ });
+
+
+ xhr.get({url: "rest/logrecords", sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data)
+ {
+ that.logData = data;
+ that.logfileGrid.update(that.logData);
+ });
+
+ };
+
+
+
+ return Broker;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Connection.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Connection.js
new file mode 100644
index 0000000000..01f9a325c5
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Connection.js
@@ -0,0 +1,213 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/_base/connect",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/formatter",
+ "qpid/common/UpdatableStore",
+ "dojo/domReady!"],
+ function (xhr, parser, query, connect, properties, updater, util, formatter, UpdatableStore) {
+
+ function Connection(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "exchange", name: name };
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+ Connection.prototype.getTitle = function()
+ {
+ return "Connection: " + this.name;
+ };
+
+ Connection.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showConnection.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.connectionUpdater = new ConnectionUpdater(contentPane.containerNode, that.modelObj, that.controller);
+
+ updater.add( that.connectionUpdater );
+
+ that.connectionUpdater.update();
+
+ }});
+ };
+
+ Connection.prototype.close = function() {
+ updater.remove( this.connectionUpdater );
+ };
+
+ function ConnectionUpdater(containerNode, connectionObj, controller)
+ {
+ var that = this;
+
+ function findNode(name) {
+ return query("." + name, containerNode)[0];
+ }
+
+ function storeNodes(names)
+ {
+ for(var i = 0; i < names.length; i++) {
+ that[names[i]] = findNode(names[i]);
+ }
+ }
+
+ storeNodes(["name",
+ "state",
+ "durable",
+ "lifetimePolicy",
+ "msgInRate",
+ "bytesInRate",
+ "bytesInRateUnits",
+ "msgOutRate",
+ "bytesOutRate",
+ "bytesOutRateUnits"]);
+
+
+
+ this.query = "rest/connection/"+ encodeURIComponent(connectionObj.parent.virtualhost.name) + "/" + encodeURIComponent(connectionObj.name);
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ that.connectionData = data[0];
+
+ util.flattenStatistics( that.connectionData );
+
+ that.updateHeader();
+ that.sessionsGrid = new UpdatableStore(that.connectionData.sessions, findNode("sessions"),
+ [ { name: "Name", field: "name", width: "70px"},
+ { name: "Mode", field: "distributionMode", width: "70px"},
+ { name: "Msgs Rate", field: "msgRate",
+ width: "150px"},
+ { name: "Bytes Rate", field: "bytesRate",
+ width: "100%"}
+ ]);
+
+
+ });
+
+ }
+
+ ConnectionUpdater.prototype.updateHeader = function()
+ {
+ this.name.innerHTML = this.connectionData[ "name" ];
+ this.state.innerHTML = this.connectionData[ "state" ];
+ this.durable.innerHTML = this.connectionData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.connectionData[ "lifetimePolicy" ];
+
+ };
+
+ ConnectionUpdater.prototype.update = function()
+ {
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ that.connectionData = data[0];
+
+ util.flattenStatistics( that.connectionData );
+
+ var sessions = that.connectionData[ "sessions" ];
+
+ that.updateHeader();
+
+ var sampleTime = new Date();
+ var messageIn = that.connectionData["messagesIn"];
+ var bytesIn = that.connectionData["bytesIn"];
+ var messageOut = that.connectionData["messagesOut"];
+ var bytesOut = that.connectionData["bytesOut"];
+
+ if(that.sampleTime)
+ {
+ var samplePeriod = sampleTime.getTime() - that.sampleTime.getTime();
+
+ var msgInRate = (1000 * (messageIn - that.messageIn)) / samplePeriod;
+ var msgOutRate = (1000 * (messageOut - that.messageOut)) / samplePeriod;
+ var bytesInRate = (1000 * (bytesIn - that.bytesIn)) / samplePeriod;
+ var bytesOutRate = (1000 * (bytesOut - that.bytesOut)) / samplePeriod;
+
+ that.msgInRate.innerHTML = msgInRate.toFixed(0);
+ var bytesInFormat = formatter.formatBytes( bytesInRate );
+ that.bytesInRate.innerHTML = "(" + bytesInFormat.value;
+ that.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
+
+ that.msgOutRate.innerHTML = msgOutRate.toFixed(0);
+ var bytesOutFormat = formatter.formatBytes( bytesOutRate );
+ that.bytesOutRate.innerHTML = "(" + bytesOutFormat.value;
+ that.bytesOutRateUnits.innerHTML = bytesOutFormat.units + "/s)";
+
+ if(sessions && that.sessions)
+ {
+ for(var i=0; i < sessions.length; i++)
+ {
+ var session = sessions[i];
+ for(var j = 0; j < that.sessions.length; j++)
+ {
+ var oldSession = that.sessions[j];
+ if(oldSession.id == session.id)
+ {
+ var msgRate = (1000 * (session.messagesOut - oldSession.messagesOut)) /
+ samplePeriod;
+ session.msgRate = msgRate.toFixed(0) + "msg/s";
+
+ var bytesRate = (1000 * (session.bytesOut - oldSession.bytesOut)) /
+ samplePeriod;
+ var bytesRateFormat = formatter.formatBytes( bytesRate );
+ session.bytesRate = bytesRateFormat.value + bytesRateFormat.units + "/s";
+ }
+
+
+ }
+
+ }
+ }
+
+ }
+
+ that.sampleTime = sampleTime;
+ that.messageIn = messageIn;
+ that.bytesIn = bytesIn;
+ that.messageOut = messageOut;
+ that.bytesOut = bytesOut;
+ that.sessions = sessions;
+
+
+ // update sessions
+ that.sessionsGrid.update(that.connectionData.sessions)
+ });
+ };
+
+
+ return Connection;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js
new file mode 100644
index 0000000000..37bae1ef8e
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js
@@ -0,0 +1,291 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/_base/connect",
+ "dijit/registry",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/formatter",
+ "qpid/common/UpdatableStore",
+ "qpid/management/addBinding",
+ "dojox/grid/EnhancedGrid",
+ "dojo/domReady!"],
+ function (xhr, parser, query, connect, registry, properties, updater, util, formatter, UpdatableStore, addBinding, EnhancedGrid) {
+
+ function Exchange(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "exchange", name: name };
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+
+ Exchange.prototype.getExchangeName = function()
+ {
+ return this.name;
+ };
+
+
+ Exchange.prototype.getVirtualHostName = function()
+ {
+ return this.modelObj.parent.virtualhost.name;
+ };
+
+ Exchange.prototype.getTitle = function()
+ {
+ return "Exchange: " + this.name;
+ };
+
+ Exchange.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showExchange.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.exchangeUpdater = new ExchangeUpdater(contentPane.containerNode, that.modelObj, that.controller);
+
+ updater.add( that.exchangeUpdater );
+
+ that.exchangeUpdater.update();
+
+
+ var addBindingButton = query(".addBindingButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(addBindingButton), "onClick",
+ function(evt){
+ addBinding.show({ virtualhost: that.getVirtualHostName(),
+ exchange: that.getExchangeName()});
+ });
+
+ var deleteBindingButton = query(".deleteBindingButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(deleteBindingButton), "onClick",
+ function(evt){
+ that.deleteBindings();
+ });
+
+ var isStandard = util.isReservedExchangeName(that.name);
+ var deleteExchangeButton = query(".deleteExchangeButton", contentPane.containerNode)[0];
+ var node = registry.byNode(deleteExchangeButton);
+ if(isStandard)
+ {
+ node.set('disabled', true);
+ }
+ else
+ {
+ connect.connect(node, "onClick",
+ function(evt){
+ that.deleteExchange();
+ });
+ }
+
+ }});
+ };
+
+ Exchange.prototype.close = function() {
+ updater.remove( this.exchangeUpdater );
+ };
+
+ Exchange.prototype.deleteBindings = function()
+ {
+ util.deleteGridSelections(
+ this.exchangeUpdater,
+ "bindingsGrid",
+ "rest/binding/"+ encodeURIComponent(this.getVirtualHostName()) + "/" + encodeURIComponent(this.name),
+ "Are you sure you want to delete binding for queue");
+ }
+
+ function ExchangeUpdater(containerNode, exchangeObj, controller)
+ {
+ var that = this;
+
+ function findNode(name) {
+ return query("." + name, containerNode)[0];
+ }
+
+ function storeNodes(names)
+ {
+ for(var i = 0; i < names.length; i++) {
+ that[names[i]] = findNode(names[i]);
+ }
+ }
+
+ storeNodes(["name",
+ "state",
+ "durable",
+ "lifetimePolicy",
+ "alertRepeatGap",
+ "alertRepeatGapUnits",
+ "alertThresholdMessageAge",
+ "alertThresholdMessageAgeUnits",
+ "alertThresholdMessageSize",
+ "alertThresholdMessageSizeUnits",
+ "alertThresholdQueueDepthBytes",
+ "alertThresholdQueueDepthBytesUnits",
+ "alertThresholdQueueDepthMessages",
+ "msgInRate",
+ "bytesInRate",
+ "bytesInRateUnits",
+ "msgDropRate",
+ "bytesDropRate",
+ "bytesDropRateUnits"]);
+
+
+
+ this.query = "rest/exchange/"+ encodeURIComponent(exchangeObj.parent.virtualhost.name) + "/" + encodeURIComponent(exchangeObj.name);
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ that.exchangeData = data[0];
+ util.flattenStatistics( that.exchangeData );
+
+ that.updateHeader();
+ that.bindingsGrid = new UpdatableStore(that.exchangeData.bindings, findNode("bindings"),
+ [ { name: "Queue", field: "queue", width: "90px"},
+ { name: "Binding Key", field: "name", width: "120px"},
+ { name: "Arguments", field: "argumentString", width: "100%"}
+ ], null, {
+ keepSelection: true,
+ plugins: {
+ pagination: {
+ pageSizes: ["10", "25", "50", "100"],
+ description: true,
+ sizeSwitch: true,
+ pageStepper: true,
+ gotoButton: true,
+ maxPageStep: 4,
+ position: "bottom"
+ },
+ indirectSelection: true
+
+ }}, EnhancedGrid);
+
+ });
+
+ }
+
+ ExchangeUpdater.prototype.updateHeader = function()
+ {
+ this.name.innerHTML = this.exchangeData[ "name" ];
+ this.state.innerHTML = this.exchangeData[ "state" ];
+ this.durable.innerHTML = this.exchangeData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.exchangeData[ "lifetimePolicy" ];
+
+ };
+
+ ExchangeUpdater.prototype.update = function()
+ {
+
+ var thisObj = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ thisObj.exchangeData = data[0];
+
+ util.flattenStatistics( thisObj.exchangeData );
+
+ var bindings = thisObj.exchangeData[ "bindings" ];
+
+ if(bindings)
+ {
+ for(var i=0; i < bindings.length; i++)
+ {
+ if(bindings[i].arguments)
+ {
+ bindings[i].argumentString = dojo.toJson(bindings[i].arguments);
+ }
+ else
+ {
+ bindings[i].argumentString = "";
+ }
+ }
+ }
+
+
+ var sampleTime = new Date();
+
+ thisObj.updateHeader();
+
+ var messageIn = thisObj.exchangeData["messagesIn"];
+ var bytesIn = thisObj.exchangeData["bytesIn"];
+ var messageDrop = thisObj.exchangeData["messagesDropped"];
+ var bytesDrop = thisObj.exchangeData["bytesDropped"];
+
+ if(thisObj.sampleTime)
+ {
+ var samplePeriod = sampleTime.getTime() - thisObj.sampleTime.getTime();
+
+ var msgInRate = (1000 * (messageIn - thisObj.messageIn)) / samplePeriod;
+ var msgDropRate = (1000 * (messageDrop - thisObj.messageDrop)) / samplePeriod;
+ var bytesInRate = (1000 * (bytesIn - thisObj.bytesIn)) / samplePeriod;
+ var bytesDropRate = (1000 * (bytesDrop - thisObj.bytesDrop)) / samplePeriod;
+
+ thisObj.msgInRate.innerHTML = msgInRate.toFixed(0);
+ var bytesInFormat = formatter.formatBytes( bytesInRate );
+ thisObj.bytesInRate.innerHTML = "(" + bytesInFormat.value;
+ thisObj.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
+
+ thisObj.msgDropRate.innerHTML = msgDropRate.toFixed(0);
+ var bytesDropFormat = formatter.formatBytes( bytesDropRate );
+ thisObj.bytesDropRate.innerHTML = "(" + bytesDropFormat.value;
+ thisObj.bytesDropRateUnits.innerHTML = bytesDropFormat.units + "/s)"
+
+ }
+
+ thisObj.sampleTime = sampleTime;
+ thisObj.messageIn = messageIn;
+ thisObj.bytesIn = bytesIn;
+ thisObj.messageDrop = messageDrop;
+ thisObj.bytesDrop = bytesDrop;
+
+ // update bindings
+ thisObj.bindingsGrid.update(thisObj.exchangeData.bindings)
+
+ });
+ };
+
+ Exchange.prototype.deleteExchange = function() {
+ if(confirm("Are you sure you want to delete exchange '" +this.name+"'?")) {
+ var query = "rest/exchange/"+ encodeURIComponent(this.getVirtualHostName()) + "/" + encodeURIComponent(this.name);
+ this.success = true
+ var that = this;
+ xhr.del({url: query, sync: true, handleAs: "json"}).then(
+ function(data) {
+ that.contentPane.onClose()
+ that.controller.tabContainer.removeChild(that.contentPane);
+ that.contentPane.destroyRecursive();
+ },
+ function(error) {that.success = false; that.failureReason = error;});
+ if(!this.success ) {
+ alert("Error:" + this.failureReason);
+ }
+ }
+ }
+
+ return Exchange;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Queue.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Queue.js
new file mode 100644
index 0000000000..8b332dbf92
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Queue.js
@@ -0,0 +1,485 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dijit/registry",
+ "dojo/_base/connect",
+ "dojo/_base/event",
+ "dojo/json",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/formatter",
+ "qpid/common/UpdatableStore",
+ "qpid/management/addBinding",
+ "qpid/management/moveCopyMessages",
+ "qpid/management/showMessage",
+ "dojo/store/JsonRest",
+ "dojox/grid/EnhancedGrid",
+ "dojo/data/ObjectStore",
+ "dojox/grid/enhanced/plugins/Pagination",
+ "dojox/grid/enhanced/plugins/IndirectSelection",
+ "dojo/domReady!"],
+ function (xhr, parser, query, registry, connect, event, json, properties, updater, util, formatter,
+ UpdatableStore, addBinding, moveMessages, showMessage, JsonRest, EnhancedGrid, ObjectStore) {
+
+ function Queue(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "queue", name: name };
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+ Queue.prototype.getQueueName = function()
+ {
+ return this.name;
+ };
+
+
+ Queue.prototype.getVirtualHostName = function()
+ {
+ return this.modelObj.parent.virtualhost.name;
+ };
+
+ Queue.prototype.getTitle = function()
+ {
+ return "Queue: " + this.name;
+ };
+
+ Queue.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showQueue.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.queueUpdater = new QueueUpdater(contentPane.containerNode, that, that.controller);
+
+ updater.add( that.queueUpdater );
+
+ that.queueUpdater.update();
+
+ var myStore = new JsonRest({target:"rest/message/"+ encodeURIComponent(that.getVirtualHostName()) +
+ "/" + encodeURIComponent(that.getQueueName())});
+ var messageGridDiv = query(".messages",contentPane.containerNode)[0];
+ that.dataStore = new ObjectStore({objectStore: myStore});
+ that.grid = new EnhancedGrid({
+ store: that.dataStore,
+ autoHeight: 10,
+ keepSelection: true,
+ structure: [
+ {name:"Size", field:"size", width: "60px"},
+ {name:"State", field:"state", width: "120px"},
+
+ {name:"Arrival", field:"arrivalTime", width: "100%",
+ formatter: function(val) {
+ var d = new Date(0);
+ d.setUTCSeconds(val/1000);
+
+ return d.toLocaleString();
+ } }
+ ],
+ plugins: {
+ pagination: {
+ pageSizes: ["10", "25", "50", "100"],
+ description: true,
+ sizeSwitch: true,
+ pageStepper: true,
+ gotoButton: true,
+ maxPageStep: 4,
+ position: "bottom"
+ },
+ indirectSelection: true
+ }
+ }, messageGridDiv);
+
+ connect.connect(that.grid, "onRowDblClick", that.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var id = that.dataStore.getValue(theItem,"id");
+ showMessage.show({ messageNumber: id,
+ queue: that.getQueueName(),
+ virtualhost: that.getVirtualHostName() });
+ });
+
+ var deleteMessagesButton = query(".deleteMessagesButton", contentPane.containerNode)[0];
+ var deleteWidget = registry.byNode(deleteMessagesButton);
+ connect.connect(deleteWidget, "onClick",
+ function(evt){
+ event.stop(evt);
+ that.deleteMessages();
+ });
+ var moveMessagesButton = query(".moveMessagesButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(moveMessagesButton), "onClick",
+ function(evt){
+ event.stop(evt);
+ that.moveOrCopyMessages({move: true});
+ });
+
+
+ var copyMessagesButton = query(".copyMessagesButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(copyMessagesButton), "onClick",
+ function(evt){
+ event.stop(evt);
+ that.moveOrCopyMessages({move: false});
+ });
+
+ var addBindingButton = query(".addBindingButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(addBindingButton), "onClick",
+ function(evt){
+ event.stop(evt);
+ addBinding.show({ virtualhost: that.getVirtualHostName(),
+ queue: that.getQueueName()});
+ });
+
+ var deleteQueueButton = query(".deleteQueueButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(deleteQueueButton), "onClick",
+ function(evt){
+ event.stop(evt);
+ that.deleteQueue();
+ });
+ }});
+
+
+
+ };
+
+ Queue.prototype.deleteMessages = function() {
+ var data = this.grid.selection.getSelected();
+ if(data.length) {
+ var that = this;
+ if(confirm("Delete " + data.length + " messages?")) {
+ var i, queryParam;
+ for(i = 0; i<data.length; i++) {
+ if(queryParam) {
+ queryParam += "&";
+ } else {
+ queryParam = "?";
+ }
+
+ queryParam += "id=" + data[i].id;
+ }
+ var query = "rest/message/"+ encodeURIComponent(that.getVirtualHostName())
+ + "/" + encodeURIComponent(that.getQueueName()) + queryParam;
+ that.success = true
+ xhr.del({url: query, sync: true, handleAs: "json"}).then(
+ function(data) {
+ that.grid.setQuery({id: "*"});
+ that.grid.selection.deselectAll();
+ that.queueUpdater.update();
+ },
+ function(error) {that.success = false; that.failureReason = error;});
+ if(!that.success ) {
+ alert("Error:" + this.failureReason);
+ }
+ }
+ }
+ };
+
+ Queue.prototype.moveOrCopyMessages = function(obj) {
+ var that = this;
+ var move = obj.move;
+ var data = this.grid.selection.getSelected();
+ if(data.length) {
+ var that = this;
+ var i, putData = { messages:[] };
+ if(move) {
+ putData.move = true;
+ }
+ for(i = 0; i<data.length; i++) {
+ putData.messages.push(data[i].id);
+ }
+ moveMessages.show({ virtualhost: this.getVirtualHostName(),
+ queue: this.getQueueName(),
+ data: putData}, function() {
+ if(move)
+ {
+ that.grid.setQuery({id: "*"});
+ that.grid.selection.deselectAll();
+ }
+ });
+
+ }
+
+
+
+ };
+
+ Queue.prototype.startup = function() {
+ this.grid.startup();
+ };
+
+ Queue.prototype.close = function() {
+ updater.remove( this.queueUpdater );
+ };
+
+ var queueTypeKeys = {
+ priority: "priorities",
+ lvq: "lvqKey",
+ sorted: "sortKey"
+ };
+
+ var queueTypeKeyNames = {
+ priority: "Number of priorities",
+ lvq: "LVQ key",
+ sorted: "Sort key"
+ };
+
+ function QueueUpdater(containerNode, queueObj, controller)
+ {
+ var that = this;
+
+ function findNode(name) {
+ return query("." + name, containerNode)[0];
+ }
+
+ function storeNodes(names)
+ {
+ for(var i = 0; i < names.length; i++) {
+ that[names[i]] = findNode(names[i]);
+ }
+ }
+
+ storeNodes(["name",
+ "state",
+ "durable",
+ "lifetimePolicy",
+ "type",
+ "typeQualifier",
+ "alertRepeatGap",
+ "alertRepeatGapUnits",
+ "alertThresholdMessageAge",
+ "alertThresholdMessageAgeUnits",
+ "alertThresholdMessageSize",
+ "alertThresholdMessageSizeUnits",
+ "alertThresholdQueueDepthBytes",
+ "alertThresholdQueueDepthBytesUnits",
+ "alertThresholdQueueDepthMessages",
+ "alternateExchange",
+ "queueDepthMessages",
+ "queueDepthBytes",
+ "queueDepthBytesUnits",
+ "unacknowledgedMessages",
+ "unacknowledgedBytes",
+ "unacknowledgedBytesUnits",
+ "msgInRate",
+ "bytesInRate",
+ "bytesInRateUnits",
+ "msgOutRate",
+ "bytesOutRate",
+ "bytesOutRateUnits"]);
+
+
+
+ this.query = "rest/queue/"+ encodeURIComponent(queueObj.getVirtualHostName()) + "/" + encodeURIComponent(queueObj.getQueueName());
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data)
+ {
+ that.queueData = data[0];
+
+ util.flattenStatistics( that.queueData );
+
+ that.updateHeader();
+ that.bindingsGrid = new UpdatableStore(that.queueData.bindings, findNode("bindings"),
+ [ { name: "Exchange", field: "exchange", width: "90px"},
+ { name: "Binding Key", field: "name", width: "120px"},
+ { name: "Arguments", field: "argumentString", width: "100%"}
+ ]);
+
+ that.consumersGrid = new UpdatableStore(that.queueData.consumers, findNode("consumers"),
+ [ { name: "Name", field: "name", width: "70px"},
+ { name: "Mode", field: "distributionMode", width: "70px"},
+ { name: "Msgs Rate", field: "msgRate",
+ width: "150px"},
+ { name: "Bytes Rate", field: "bytesRate",
+ width: "100%"}
+ ]);
+
+
+
+
+ });
+
+ }
+
+ QueueUpdater.prototype.updateHeader = function()
+ {
+
+ var bytesDepth;
+ this.name.innerHTML = this.queueData[ "name" ];
+ this.state.innerHTML = this.queueData[ "state" ];
+ this.durable.innerHTML = this.queueData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.queueData[ "lifetimePolicy" ];
+ this.alternateExchange.innerHTML = this.queueData[ "alternateExchange" ] ? this.queueData[ "alternateExchange" ]: "" ;
+
+ this.queueDepthMessages.innerHTML = this.queueData["queueDepthMessages"];
+ bytesDepth = formatter.formatBytes( this.queueData["queueDepthBytes"] );
+ this.queueDepthBytes.innerHTML = "(" + bytesDepth.value;
+ this.queueDepthBytesUnits.innerHTML = bytesDepth.units + ")";
+
+ this.unacknowledgedMessages.innerHTML = this.queueData["unacknowledgedMessages"];
+ bytesDepth = formatter.formatBytes( this.queueData["unacknowledgedBytes"] );
+ this.unacknowledgedBytes.innerHTML = "(" + bytesDepth.value;
+ this.unacknowledgedBytesUnits.innerHTML = bytesDepth.units + ")";
+ this.type.innerHTML = this.queueData[ "type" ];
+ if (this.queueData.type == "standard")
+ {
+ this.typeQualifier.style.display = "none";
+ }
+ else
+ {
+ this.typeQualifier.innerHTML = "(" + queueTypeKeyNames[this.queueData.type] + ": " + this.queueData[queueTypeKeys[this.queueData.type]] + ")";
+ }
+
+ };
+
+ QueueUpdater.prototype.update = function()
+ {
+
+ var thisObj = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) {
+ var i,j;
+ thisObj.queueData = data[0];
+ util.flattenStatistics( thisObj.queueData );
+
+ var bindings = thisObj.queueData[ "bindings" ];
+ var consumers = thisObj.queueData[ "consumers" ];
+
+ for(i=0; i < bindings.length; i++) {
+ bindings[i].argumentString = json.stringify(bindings[i].arguments);
+ }
+
+ thisObj.updateHeader();
+
+
+ // update alerting info
+ var alertRepeatGap = formatter.formatTime( thisObj.queueData["alertRepeatGap"] );
+
+ thisObj.alertRepeatGap.innerHTML = alertRepeatGap.value;
+ thisObj.alertRepeatGapUnits.innerHTML = alertRepeatGap.units;
+
+
+ var alertMsgAge = formatter.formatTime( thisObj.queueData["alertThresholdMessageAge"] );
+
+ thisObj.alertThresholdMessageAge.innerHTML = alertMsgAge.value;
+ thisObj.alertThresholdMessageAgeUnits.innerHTML = alertMsgAge.units;
+
+ var alertMsgSize = formatter.formatBytes( thisObj.queueData["alertThresholdMessageSize"] );
+
+ thisObj.alertThresholdMessageSize.innerHTML = alertMsgSize.value;
+ thisObj.alertThresholdMessageSizeUnits.innerHTML = alertMsgSize.units;
+
+ var alertQueueDepth = formatter.formatBytes( thisObj.queueData["alertThresholdQueueDepthBytes"] );
+
+ thisObj.alertThresholdQueueDepthBytes.innerHTML = alertQueueDepth.value;
+ thisObj.alertThresholdQueueDepthBytesUnits.innerHTML = alertQueueDepth.units;
+
+ thisObj.alertThresholdQueueDepthMessages.innerHTML = thisObj.queueData["alertThresholdQueueDepthMessages"];
+
+ var sampleTime = new Date();
+ var messageIn = thisObj.queueData["totalEnqueuedMessages"];
+ var bytesIn = thisObj.queueData["totalEnqueuedBytes"];
+ var messageOut = thisObj.queueData["totalDequeuedMessages"];
+ var bytesOut = thisObj.queueData["totalDequeuedBytes"];
+
+ if(thisObj.sampleTime) {
+ var samplePeriod = sampleTime.getTime() - thisObj.sampleTime.getTime();
+
+ var msgInRate = (1000 * (messageIn - thisObj.messageIn)) / samplePeriod;
+ var msgOutRate = (1000 * (messageOut - thisObj.messageOut)) / samplePeriod;
+ var bytesInRate = (1000 * (bytesIn - thisObj.bytesIn)) / samplePeriod;
+ var bytesOutRate = (1000 * (bytesOut - thisObj.bytesOut)) / samplePeriod;
+
+ thisObj.msgInRate.innerHTML = msgInRate.toFixed(0);
+ var bytesInFormat = formatter.formatBytes( bytesInRate );
+ thisObj.bytesInRate.innerHTML = "(" + bytesInFormat.value;
+ thisObj.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
+
+ thisObj.msgOutRate.innerHTML = msgOutRate.toFixed(0);
+ var bytesOutFormat = formatter.formatBytes( bytesOutRate );
+ thisObj.bytesOutRate.innerHTML = "(" + bytesOutFormat.value;
+ thisObj.bytesOutRateUnits.innerHTML = bytesOutFormat.units + "/s)";
+
+ if(consumers && thisObj.consumers) {
+ for(i=0; i < consumers.length; i++) {
+ var consumer = consumers[i];
+ for(j = 0; j < thisObj.consumers.length; j++) {
+ var oldConsumer = thisObj.consumers[j];
+ if(oldConsumer.id == consumer.id) {
+ var msgRate = (1000 * (consumer.messagesOut - oldConsumer.messagesOut)) /
+ samplePeriod;
+ consumer.msgRate = msgRate.toFixed(0) + "msg/s";
+
+ var bytesRate = (1000 * (consumer.bytesOut - oldConsumer.bytesOut)) /
+ samplePeriod;
+ var bytesRateFormat = formatter.formatBytes( bytesRate );
+ consumer.bytesRate = bytesRateFormat.value + bytesRateFormat.units + "/s";
+ }
+ }
+ }
+ }
+
+ }
+
+ thisObj.sampleTime = sampleTime;
+ thisObj.messageIn = messageIn;
+ thisObj.bytesIn = bytesIn;
+ thisObj.messageOut = messageOut;
+ thisObj.bytesOut = bytesOut;
+ thisObj.consumers = consumers;
+
+ // update bindings
+ thisObj.bindingsGrid.update(thisObj.queueData.bindings);
+
+ // update consumers
+ thisObj.consumersGrid.update(thisObj.queueData.consumers)
+
+ });
+ };
+
+ Queue.prototype.deleteQueue = function() {
+ if(confirm("Are you sure you want to delete queue '" +this.name+"'?")) {
+ var query = "rest/queue/"+ encodeURIComponent(this.getVirtualHostName()) + "/" + encodeURIComponent(this.name);
+ this.success = true
+ var that = this;
+ xhr.del({url: query, sync: true, handleAs: "json"}).then(
+ function(data) {
+ that.contentPane.onClose()
+ that.controller.tabContainer.removeChild(that.contentPane);
+ that.contentPane.destroyRecursive();
+ },
+ function(error) {that.success = false; that.failureReason = error;});
+ if(!this.success ) {
+ alert("Error:" + this.failureReason);
+ }
+ }
+ }
+
+ return Queue;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js
new file mode 100644
index 0000000000..957f2381cf
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js
@@ -0,0 +1,378 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/_base/connect",
+ "dijit/registry",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/util",
+ "qpid/common/formatter",
+ "qpid/common/UpdatableStore",
+ "qpid/management/addQueue",
+ "qpid/management/addExchange",
+ "dojox/grid/EnhancedGrid",
+ "dojo/domReady!"],
+ function (xhr, parser, query, connect, registry, properties, updater, util, formatter, UpdatableStore, addQueue, addExchange, EnhancedGrid) {
+
+ function VirtualHost(name, parent, controller) {
+ this.name = name;
+ this.controller = controller;
+ this.modelObj = { type: "virtualhost", name: name};
+ if(parent) {
+ this.modelObj.parent = {};
+ this.modelObj.parent[ parent.type] = parent;
+ }
+ }
+
+ VirtualHost.prototype.getTitle = function()
+ {
+ return "VirtualHost: " + this.name;
+ };
+
+ VirtualHost.prototype.open = function(contentPane) {
+ var that = this;
+ this.contentPane = contentPane;
+ xhr.get({url: "showVirtualHost.html",
+ sync: true,
+ load: function(data) {
+ contentPane.containerNode.innerHTML = data;
+ parser.parse(contentPane.containerNode);
+
+ that.vhostUpdater = new Updater(contentPane.containerNode, that.modelObj, that.controller);
+
+ updater.add( that.vhostUpdater );
+
+ that.vhostUpdater.update();
+
+ var addQueueButton = query(".addQueueButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(addQueueButton), "onClick", function(evt){ addQueue.show(that.name) });
+
+ var deleteQueueButton = query(".deleteQueueButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(deleteQueueButton), "onClick",
+ function(evt){
+ util.deleteGridSelections(
+ that.vhostUpdater,
+ "queuesGrid",
+ "rest/queue/"+ encodeURIComponent(that.name),
+ "Are you sure you want to delete queue");
+ }
+ );
+
+ var addExchangeButton = query(".addExchangeButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(addExchangeButton), "onClick", function(evt){ addExchange.show(that.name) });
+
+ var deleteExchangeButton = query(".deleteExchangeButton", contentPane.containerNode)[0];
+ connect.connect(registry.byNode(deleteExchangeButton), "onClick",
+ function(evt)
+ {
+ util.deleteGridSelections(
+ that.vhostUpdater,
+ "exchangesGrid",
+ "rest/exchange/"+ encodeURIComponent(that.name),
+ "Are you sure you want to delete exchange");
+ }
+ );
+ }});
+
+ };
+
+ VirtualHost.prototype.close = function() {
+ updater.remove( this.vhostUpdater );
+ };
+
+ function Updater(node, vhost, controller)
+ {
+
+ var that = this;
+
+ function findNode(name) {
+ return query("." + name, node)[0];
+ }
+
+ function storeNodes(names)
+ {
+ for(var i = 0; i < names.length; i++) {
+ that[names[i]] = findNode(names[i]);
+ }
+ }
+
+ storeNodes(["name",
+ "state",
+ "durable",
+ "lifetimePolicy",
+ "alertRepeatGap",
+ "alertRepeatGapUnits",
+ "alertThresholdMessageAge",
+ "alertThresholdMessageAgeUnits",
+ "alertThresholdMessageSize",
+ "alertThresholdMessageSizeUnits",
+ "alertThresholdQueueDepthBytes",
+ "alertThresholdQueueDepthBytesUnits",
+ "alertThresholdQueueDepthMessages",
+ "msgInRate",
+ "bytesInRate",
+ "bytesInRateUnits",
+ "msgOutRate",
+ "bytesOutRate",
+ "bytesOutRateUnits"]);
+
+ this.query = "rest/virtualhost/"+ encodeURIComponent(vhost.name);
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) {
+ that.vhostData = data[0];
+
+ // flatten statistics into attributes
+ util.flattenStatistics( that.vhostData );
+
+ var gridProperties = {
+ keepSelection: true,
+ plugins: {
+ pagination: {
+ pageSizes: ["10", "25", "50", "100"],
+ description: true,
+ sizeSwitch: true,
+ pageStepper: true,
+ gotoButton: true,
+ maxPageStep: 4,
+ position: "bottom"
+ },
+ indirectSelection: true
+
+ }};
+
+ that.updateHeader();
+ that.queuesGrid = new UpdatableStore(that.vhostData.queues, findNode("queues"),
+ [ { name: "Name", field: "name", width: "90px"},
+ { name: "Messages", field: "queueDepthMessages", width: "90px"},
+ { name: "Arguments", field: "arguments", width: "100%"}
+ ],
+ function(obj)
+ {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var queueName = obj.dataStore.getValue(theItem,"name");
+ controller.show("queue", queueName, vhost);
+ });
+ } , gridProperties, EnhancedGrid);
+
+ that.exchangesGrid = new UpdatableStore(that.vhostData.exchanges, findNode("exchanges"),
+ [
+ { name: "Name", field: "name", width: "120px"},
+ { name: "Type", field: "type", width: "120px"},
+ { name: "Binding Count", field: "bindingCount", width: "100%"}
+ ],
+ function(obj)
+ {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var exchangeName = obj.dataStore.getValue(theItem,"name");
+ controller.show("exchange", exchangeName, vhost);
+ });
+ } , gridProperties, EnhancedGrid);
+
+
+ that.connectionsGrid = new UpdatableStore(that.vhostData.connections,
+ findNode("connections"),
+ [ { name: "Name", field: "name", width: "150px"},
+ { name: "Sessions", field: "sessionCount", width: "70px"},
+ { name: "Msgs In", field: "msgInRate",
+ width: "80px"},
+ { name: "Bytes In", field: "bytesInRate",
+ width: "80px"},
+ { name: "Msgs Out", field: "msgOutRate",
+ width: "80px"},
+ { name: "Bytes Out", field: "bytesOutRate",
+ width: "100%"}
+ ],
+ function(obj)
+ {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var connectionName = obj.dataStore.getValue(theItem,"name");
+ controller.show("connection", connectionName, vhost);
+ });
+ } );
+
+
+
+ });
+
+ }
+
+ Updater.prototype.updateHeader = function()
+ {
+ this.name.innerHTML = this.vhostData[ "name" ];
+ this.state.innerHTML = this.vhostData[ "state" ];
+ this.durable.innerHTML = this.vhostData[ "durable" ];
+ this.lifetimePolicy.innerHTML = this.vhostData[ "lifetimePolicy" ];
+
+
+ };
+
+ Updater.prototype.update = function()
+ {
+
+ var thisObj = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data) {
+ thisObj.vhostData = data[0];
+ util.flattenStatistics( thisObj.vhostData );
+ var connections = thisObj.vhostData[ "connections" ];
+ var queues = thisObj.vhostData[ "queues" ];
+ var exchanges = thisObj.vhostData[ "exchanges" ];
+
+ thisObj.updateHeader();
+
+
+ // update alerting info
+ var alertRepeatGap = formatter.formatTime( thisObj.vhostData["alertRepeatGap"] );
+
+ thisObj.alertRepeatGap.innerHTML = alertRepeatGap.value;
+ thisObj.alertRepeatGapUnits.innerHTML = alertRepeatGap.units;
+
+
+ var alertMsgAge = formatter.formatTime( thisObj.vhostData["alertThresholdMessageAge"] );
+
+ thisObj.alertThresholdMessageAge.innerHTML = alertMsgAge.value;
+ thisObj.alertThresholdMessageAgeUnits.innerHTML = alertMsgAge.units;
+
+ var alertMsgSize = formatter.formatBytes( thisObj.vhostData["alertThresholdMessageSize"] );
+
+ thisObj.alertThresholdMessageSize.innerHTML = alertMsgSize.value;
+ thisObj.alertThresholdMessageSizeUnits.innerHTML = alertMsgSize.units;
+
+ var alertQueueDepth = formatter.formatBytes( thisObj.vhostData["alertThresholdQueueDepthBytes"] );
+
+ thisObj.alertThresholdQueueDepthBytes.innerHTML = alertQueueDepth.value;
+ thisObj.alertThresholdQueueDepthBytesUnits.innerHTML = alertQueueDepth.units;
+
+ thisObj.alertThresholdQueueDepthMessages.innerHTML = thisObj.vhostData["alertThresholdQueueDepthMessages"];
+
+ var stats = thisObj.vhostData[ "statistics" ];
+
+ var sampleTime = new Date();
+ var messageIn = stats["messagesIn"];
+ var bytesIn = stats["bytesIn"];
+ var messageOut = stats["messagesOut"];
+ var bytesOut = stats["bytesOut"];
+
+ if(thisObj.sampleTime)
+ {
+ var samplePeriod = sampleTime.getTime() - thisObj.sampleTime.getTime();
+
+ var msgInRate = (1000 * (messageIn - thisObj.messageIn)) / samplePeriod;
+ var msgOutRate = (1000 * (messageOut - thisObj.messageOut)) / samplePeriod;
+ var bytesInRate = (1000 * (bytesIn - thisObj.bytesIn)) / samplePeriod;
+ var bytesOutRate = (1000 * (bytesOut - thisObj.bytesOut)) / samplePeriod;
+
+ thisObj.msgInRate.innerHTML = msgInRate.toFixed(0);
+ var bytesInFormat = formatter.formatBytes( bytesInRate );
+ thisObj.bytesInRate.innerHTML = "(" + bytesInFormat.value;
+ thisObj.bytesInRateUnits.innerHTML = bytesInFormat.units + "/s)";
+
+ thisObj.msgOutRate.innerHTML = msgOutRate.toFixed(0);
+ var bytesOutFormat = formatter.formatBytes( bytesOutRate );
+ thisObj.bytesOutRate.innerHTML = "(" + bytesOutFormat.value;
+ thisObj.bytesOutRateUnits.innerHTML = bytesOutFormat.units + "/s)";
+
+ if(connections && thisObj.connections)
+ {
+ for(var i=0; i < connections.length; i++)
+ {
+ var connection = connections[i];
+ for(var j = 0; j < thisObj.connections.length; j++)
+ {
+ var oldConnection = thisObj.connections[j];
+ if(oldConnection.id == connection.id)
+ {
+ msgOutRate = (1000 * (connection.messagesOut - oldConnection.messagesOut)) /
+ samplePeriod;
+ connection.msgOutRate = msgOutRate.toFixed(0) + "msg/s";
+
+ bytesOutRate = (1000 * (connection.bytesOut - oldConnection.bytesOut)) /
+ samplePeriod;
+ var bytesOutRateFormat = formatter.formatBytes( bytesOutRate );
+ connection.bytesOutRate = bytesOutRateFormat.value + bytesOutRateFormat.units + "/s";
+
+
+ msgInRate = (1000 * (connection.messagesIn - oldConnection.messagesIn)) /
+ samplePeriod;
+ connection.msgInRate = msgInRate.toFixed(0) + "msg/s";
+
+ bytesInRate = (1000 * (connection.bytesIn - oldConnection.bytesIn)) /
+ samplePeriod;
+ var bytesInRateFormat = formatter.formatBytes( bytesInRate );
+ connection.bytesInRate = bytesInRateFormat.value + bytesInRateFormat.units + "/s";
+ }
+
+
+ }
+
+ }
+ }
+ }
+
+ thisObj.sampleTime = sampleTime;
+ thisObj.messageIn = messageIn;
+ thisObj.bytesIn = bytesIn;
+ thisObj.messageOut = messageOut;
+ thisObj.bytesOut = bytesOut;
+ thisObj.connections = connections;
+
+ // update queues
+ thisObj.queuesGrid.update(thisObj.vhostData.queues);
+
+ // update exchanges
+ thisObj.exchangesGrid.update(thisObj.vhostData.exchanges);
+
+ var exchangesGrid = thisObj.exchangesGrid.grid;
+ for(var i=0; i< thisObj.vhostData.exchanges.length; i++)
+ {
+ var data = exchangesGrid.getItem(i);
+ var isStandard = false;
+ if (data && data.name)
+ {
+ isStandard = util.isReservedExchangeName(data.name);
+ }
+ exchangesGrid.rowSelectCell.setDisabled(i, isStandard);
+ }
+
+ // update connections
+ thisObj.connectionsGrid.update(thisObj.vhostData.connections)
+
+
+ });
+ };
+
+
+ return VirtualHost;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addBinding.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addBinding.js
new file mode 100644
index 0000000000..83e724d0e9
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addBinding.js
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/dom-construct",
+ "dojo/_base/window",
+ "dijit/registry",
+ "dojo/parser",
+ "dojo/_base/array",
+ "dojo/_base/event",
+ 'dojo/_base/json',
+ "dojo/store/Memory",
+ "dijit/form/FilteringSelect",
+ "dijit/form/NumberSpinner", // required by the form
+ /* dojox/ validate resources */
+ "dojox/validate/us", "dojox/validate/web",
+ /* basic dijit classes */
+ "dijit/Dialog",
+ "dijit/form/CheckBox", "dijit/form/Textarea",
+ "dijit/form/FilteringSelect", "dijit/form/TextBox",
+ "dijit/form/ValidationTextBox", "dijit/form/DateTextBox",
+ "dijit/form/TimeTextBox", "dijit/form/Button",
+ "dijit/form/RadioButton", "dijit/form/Form",
+ "dijit/form/DateTextBox",
+ /* basic dojox classes */
+ "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect",
+ "dojo/domReady!"],
+ function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, FilteringSelect) {
+
+ var addBinding = {};
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ var convertToBinding = function convertToBinding(formValues)
+ {
+ var newBinding = {};
+
+ newBinding.name = formValues.name;
+ for(var propName in formValues)
+ {
+ if(formValues.hasOwnProperty(propName))
+ {
+ if(propName === "durable")
+ {
+ if (formValues.durable[0] && formValues.durable[0] == "durable") {
+ newBinding.durable = true;
+ }
+ } else {
+ if(formValues[ propName ] !== "") {
+ newBinding[ propName ] = formValues[propName];
+ }
+ }
+
+ }
+ }
+ if(addBinding.queue) {
+ newBinding.queue = addBinding.queue;
+ }
+ if(addBinding.exchange) {
+ newBinding.exchange = addBinding.exchange;
+ }
+ return newBinding;
+ };
+
+
+ xhr.get({url: "addBinding.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ node.innerHTML = data;
+ addBinding.dialogNode = dom.byId("addBinding");
+ parser.instantiate([addBinding.dialogNode]);
+
+ theForm = registry.byId("formAddBinding");
+ array.forEach(theForm.getDescendants(), function(widget)
+ {
+ if(widget.name === "type") {
+ widget.on("change", function(isChecked) {
+
+ var obj = registry.byId(widget.id + ":fields");
+ if(obj) {
+ if(isChecked) {
+ obj.domNode.style.display = "block";
+ obj.resize();
+ } else {
+ obj.domNode.style.display = "none";
+ obj.resize();
+ }
+ }
+ })
+ }
+
+ });
+
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ var newBinding = convertToBinding(theForm.getValues());
+ var that = this;
+
+ xhr.put({url: "rest/binding/"+encodeURIComponent(addBinding.vhost)
+ +"/"+encodeURIComponent(newBinding.exchange)
+ +"/"+encodeURIComponent(newBinding.queue)
+ +"/"+encodeURIComponent(newBinding.name),
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newBinding),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(this.success === true)
+ {
+ registry.byId("addBinding").hide();
+ }
+ else
+ {
+ alert("Error:" + this.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+ }});
+
+ addBinding.show = function(obj) {
+ var that = this;
+
+ addBinding.vhost = obj.virtualhost;
+ addBinding.queue = obj.queue;
+ addBinding.exchange = obj.exchange;
+ registry.byId("formAddBinding").reset();
+
+
+
+ xhr.get({url: "rest/queue/" + encodeURIComponent(obj.virtualhost) + "?depth=0",
+ handleAs: "json"}).then(
+ function(data) {
+ var queues = [];
+ for(var i=0; i < data.length; i++) {
+ queues[i] = {id: data[i].name, name: data[i].name};
+ }
+ var queueStore = new Memory({ data: queues });
+
+
+ if(that.queueChooser) {
+ that.queueChooser.destroy( false );
+ }
+ var queueDiv = dom.byId("addBinding.selectQueueDiv");
+ var input = construct.create("input", {id: "addBindingSelectQueue"}, queueDiv);
+
+ that.queueChooser = new FilteringSelect({ id: "addBindingSelectQueue",
+ name: "queue",
+ store: queueStore,
+ searchAttr: "name"}, input);
+
+ if(obj.queue)
+ {
+ that.queueChooser.set("value", obj.queue);
+ that.queueChooser.set("disabled", true);
+ }
+
+ xhr.get({url: "rest/exchange/" + encodeURIComponent(obj.virtualhost) + "?depth=0",
+ handleAs: "json"}).then(
+ function(data) {
+
+ var exchanges = [];
+ for(var i=0; i < data.length; i++) {
+ exchanges[i] = {id: data[i].name, name: data[i].name};
+ }
+ var exchangeStore = new Memory({ data: exchanges });
+
+
+ if(that.exchangeChooser) {
+ that.exchangeChooser.destroy( false );
+ }
+ var exchangeDiv = dom.byId("addBinding.selectExchangeDiv");
+ var input = construct.create("input", {id: "addBindingSelectExchange"}, exchangeDiv);
+
+ that.exchangeChooser = new FilteringSelect({ id: "addBindingSelectExchange",
+ name: "exchange",
+ store: exchangeStore,
+ searchAttr: "name"}, input);
+
+ if(obj.exchange)
+ {
+ that.exchangeChooser.set("value", obj.exchange);
+ that.exchangeChooser.set("disabled", true);
+ }
+
+
+ registry.byId("addBinding").show();
+ });
+
+
+ });
+
+
+ };
+
+ return addBinding;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addExchange.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addExchange.js
new file mode 100644
index 0000000000..915092a9d1
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addExchange.js
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/dom-construct",
+ "dojo/_base/window",
+ "dijit/registry",
+ "dojo/parser",
+ "dojo/_base/array",
+ "dojo/_base/event",
+ 'dojo/_base/json',
+ "dijit/form/NumberSpinner", // required by the form
+ /* dojox/ validate resources */
+ "dojox/validate/us", "dojox/validate/web",
+ /* basic dijit classes */
+ "dijit/Dialog",
+ "dijit/form/CheckBox", "dijit/form/Textarea",
+ "dijit/form/FilteringSelect", "dijit/form/TextBox",
+ "dijit/form/ValidationTextBox", "dijit/form/DateTextBox",
+ "dijit/form/TimeTextBox", "dijit/form/Button",
+ "dijit/form/RadioButton", "dijit/form/Form",
+ "dijit/form/DateTextBox",
+ /* basic dojox classes */
+ "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect",
+ "dojo/domReady!"],
+ function (xhr, dom, construct, win, registry, parser, array, event, json) {
+
+ var addExchange = {};
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ var convertToExchange = function convertToExchange(formValues)
+ {
+ var newExchange = {};
+ newExchange.name = formValues.name;
+ for(var propName in formValues)
+ {
+ if(formValues.hasOwnProperty(propName))
+ {
+ if(propName === "durable")
+ {
+ if (formValues.durable[0] && formValues.durable[0] == "durable") {
+ newExchange.durable = true;
+ }
+ } else {
+ if(formValues[ propName ] !== "") {
+ newExchange[ propName ] = formValues[propName];
+ }
+ }
+
+ }
+ }
+
+ return newExchange;
+ };
+
+
+ xhr.get({url: "addExchange.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ node.innerHTML = data;
+ addExchange.dialogNode = dom.byId("addExchange");
+ parser.instantiate([addExchange.dialogNode]);
+
+ theForm = registry.byId("formAddExchange");
+ array.forEach(theForm.getDescendants(), function(widget)
+ {
+ if(widget.name === "type") {
+ widget.on("change", function(isChecked) {
+
+ var obj = registry.byId(widget.id + ":fields");
+ if(obj) {
+ if(isChecked) {
+ obj.domNode.style.display = "block";
+ obj.resize();
+ } else {
+ obj.domNode.style.display = "none";
+ obj.resize();
+ }
+ }
+ })
+ }
+
+ });
+
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ var newExchange = convertToExchange(theForm.getValues());
+ var that = this;
+ xhr.put({url: "rest/exchange/"+encodeURIComponent(addExchange.vhost) +
+ "/"+encodeURIComponent(newExchange.name), sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newExchange),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(this.success === true)
+ {
+ registry.byId("addExchange").hide();
+ }
+ else
+ {
+ alert("Error:" + this.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+ }});
+
+ addExchange.show = function(vhost) {
+ addExchange.vhost = vhost;
+ registry.byId("formAddExchange").reset();
+ registry.byId("addExchange").show();
+ };
+
+ return addExchange;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addQueue.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addQueue.js
new file mode 100644
index 0000000000..53f82aff48
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addQueue.js
@@ -0,0 +1,191 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/dom-construct",
+ "dojo/_base/window",
+ "dijit/registry",
+ "dojo/parser",
+ "dojo/_base/array",
+ "dojo/_base/event",
+ 'dojo/_base/json',
+ "dijit/form/NumberSpinner", // required by the form
+ /* dojox/ validate resources */
+ "dojox/validate/us", "dojox/validate/web",
+ /* basic dijit classes */
+ "dijit/Dialog",
+ "dijit/form/CheckBox", "dijit/form/Textarea",
+ "dijit/form/FilteringSelect", "dijit/form/TextBox",
+ "dijit/form/ValidationTextBox", "dijit/form/DateTextBox",
+ "dijit/form/TimeTextBox", "dijit/form/Button",
+ "dijit/form/RadioButton", "dijit/form/Form",
+ "dijit/form/DateTextBox",
+ /* basic dojox classes */
+ "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect",
+ "dojo/domReady!"],
+ function (xhr, dom, construct, win, registry, parser, array, event, json) {
+
+ var addQueue = {};
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ var typeSpecificFields = {
+ priorities: "priority",
+ lvqKey: "lvq",
+ sortKey: "sorted"
+ };
+
+ var requiredFields = {
+ priority: "priorities",
+ sorted: "sortkey"
+ };
+
+ var fieldConverters = {
+ queueFlowControlSizeBytes: parseInt,
+ queueFlowResumeSizeBytes: parseInt,
+ alertThresholdMessageSize: parseInt,
+ alertThresholdQueueDepthMessages: parseInt,
+ maximumDeliveryAttempts: parseInt,
+ alertThresholdMessageAge: parseInt,
+ alertRepeatGap: parseInt
+ }
+
+ var convertToQueue = function convertToQueue(formValues)
+ {
+ var newQueue = {};
+ newQueue.name = formValues.name;
+ for(var propName in formValues)
+ {
+ if(formValues.hasOwnProperty(propName))
+ {
+ if(propName === "durable")
+ {
+ if (formValues.durable[0] && formValues.durable[0] == "durable") {
+ newQueue.durable = true;
+ }
+ }
+ else if(propName === "dlqEnabled")
+ {
+ if (formValues.dlqEnabled[0] && formValues.dlqEnabled[0] == "dlqEnabled") {
+ newQueue["x-qpid-dlq-enabled"] = true;
+ }
+ }
+ else if (!typeSpecificFields.hasOwnProperty(propName) ||
+ formValues.type === typeSpecificFields[ propName ]) {
+ if(formValues[ propName ] !== "") {
+ if (fieldConverters.hasOwnProperty(propName))
+ {
+ newQueue[ propName ] = fieldConverters[propName](formValues[propName]);
+ }
+ else
+ {
+ newQueue[ propName ] = formValues[propName];
+ }
+ }
+ }
+
+ }
+ }
+
+ return newQueue;
+ };
+
+
+ xhr.get({url: "addQueue.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ node.innerHTML = data;
+ addQueue.dialogNode = dom.byId("addQueue");
+ parser.instantiate([addQueue.dialogNode]);
+
+ // for children which have name type, add a function to make all the associated rows
+ // visible / invisible as the radio button is checked / unchecked
+
+ theForm = registry.byId("formAddQueue");
+ array.forEach(theForm.getDescendants(), function(widget)
+ {
+ if(widget.name === "type") {
+ widget.on("change", function(isChecked) {
+
+ var objId = widget.id + ":fields";
+ var obj = registry.byId(objId);
+ if(obj) {
+ if(isChecked) {
+ obj.domNode.style.display = "block";
+ } else {
+ obj.domNode.style.display = "none";
+ }
+ obj.resize();
+ var widgetValue = widget.value;
+ if (requiredFields.hasOwnProperty(widgetValue))
+ {
+ dijit.byId('formAddQueue.' + requiredFields[widgetValue]).required = isChecked;
+ }
+ }
+ })
+ }
+
+ });
+
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ var newQueue = convertToQueue(theForm.getValues());
+ var that = this;
+
+ xhr.put({url: "rest/queue/"+encodeURIComponent(addQueue.vhost)
+ +"/"+encodeURIComponent(newQueue.name), sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newQueue),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(this.success === true)
+ {
+ registry.byId("addQueue").hide();
+ }
+ else
+ {
+ alert("Error:" + this.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+ }});
+
+ addQueue.show = function(vhost) {
+ addQueue.vhost = vhost;
+ registry.byId("formAddQueue").reset();
+ registry.byId("addQueue").show();
+ };
+
+ return addQueue;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js
new file mode 100644
index 0000000000..8e5ac862bd
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js
@@ -0,0 +1,327 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/parser",
+ "dojo/query",
+ "dojo/dom-construct",
+ "dojo/_base/connect",
+ "dojo/_base/window",
+ "dojo/_base/event",
+ "dojo/_base/json",
+ "dijit/registry",
+ "qpid/common/util",
+ "qpid/common/properties",
+ "qpid/common/updater",
+ "qpid/common/UpdatableStore",
+ "dojox/grid/EnhancedGrid",
+ "dojox/grid/enhanced/plugins/Pagination",
+ "dojox/grid/enhanced/plugins/IndirectSelection",
+ "dojox/validate/us", "dojox/validate/web",
+ "dijit/Dialog",
+ "dijit/form/TextBox",
+ "dijit/form/ValidationTextBox",
+ "dijit/form/TimeTextBox", "dijit/form/Button",
+ "dijit/form/Form",
+ "dijit/form/DateTextBox",
+ "dojo/domReady!"],
+ function (xhr, dom, parser, query, construct, connect, win, event, json, registry, util, properties, updater, UpdatableStore, EnhancedGrid) {
+ function DatabaseAuthManager(containerNode, authProviderObj, controller) {
+ var node = construct.create("div", null, containerNode, "last");
+ var that = this;
+ this.name = authProviderObj.name;
+ xhr.get({url: "authenticationprovider/showPrincipalDatabaseAuthenticationManager.html",
+ sync: true,
+ load: function(data) {
+ node.innerHTML = data;
+ parser.parse(node);
+
+
+ that.authDatabaseUpdater= new AuthProviderUpdater(node, authProviderObj, controller);
+
+ updater.add( that.authDatabaseUpdater);
+
+ that.authDatabaseUpdater.update();
+
+
+ }});
+ }
+
+ DatabaseAuthManager.prototype.update = function() {
+ this.authDatabaseUpdater.update();
+ };
+
+ DatabaseAuthManager.prototype.close = function() {
+ updater.remove( this.authDatabaseUpdater );
+ };
+
+ function AuthProviderUpdater(node, authProviderObj, controller)
+ {
+ this.controller = controller;
+ this.query = "rest/authenticationprovider/"+encodeURIComponent(authProviderObj.name);
+ this.name = authProviderObj.name;
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data) {
+ that.authProviderData = data[0];
+
+ util.flattenStatistics( that.authProviderData );
+
+ var userDiv = query(".users")[0];
+
+ var gridProperties = {
+ height: 400,
+ keepSelection: true,
+ plugins: {
+ pagination: {
+ pageSizes: ["10", "25", "50", "100"],
+ description: true,
+ sizeSwitch: true,
+ pageStepper: true,
+ gotoButton: true,
+ maxPageStep: 4,
+ position: "bottom"
+ },
+ indirectSelection: true
+
+ }};
+
+
+ that.usersGrid =
+ new UpdatableStore(that.authProviderData.users, userDiv,
+ [ { name: "User Name", field: "name", width: "100%" }
+ ], function(obj) {
+ connect.connect(obj.grid, "onRowDblClick", obj.grid,
+ function(evt){
+ var idx = evt.rowIndex,
+ theItem = this.getItem(idx);
+ var name = obj.dataStore.getValue(theItem,"name");
+ var id = obj.dataStore.getValue(theItem,"id");
+ setPassword.show(authProviderObj.name, {name: name, id: id});
+ });
+ }, gridProperties, EnhancedGrid);
+
+
+ var addUserButton = query(".addUserButton", node)[0];
+ connect.connect(registry.byNode(addUserButton), "onClick", function(evt){ addUser.show(authProviderObj.name) });
+
+ var deleteMessagesButton = query(".deleteUserButton", node)[0];
+ var deleteWidget = registry.byNode(deleteMessagesButton);
+ connect.connect(deleteWidget, "onClick",
+ function(evt){
+ event.stop(evt);
+ that.deleteUsers();
+ });
+ });
+ }
+
+ AuthProviderUpdater.prototype.deleteUsers = function()
+ {
+ var grid = this.usersGrid.grid;
+ var data = grid.selection.getSelected();
+ if(data.length) {
+ var that = this;
+ if(confirm("Delete " + data.length + " users?")) {
+ var i, queryParam;
+ for(i = 0; i<data.length; i++) {
+ if(queryParam) {
+ queryParam += "&";
+ } else {
+ queryParam = "?";
+ }
+
+ queryParam += "id=" + data[i].id;
+ }
+ var query = "rest/user/"+ encodeURIComponent(that.name)
+ + queryParam;
+ that.success = true
+ xhr.del({url: query, sync: true, handleAs: "json"}).then(
+ function(data) {
+ grid.setQuery({id: "*"});
+ grid.selection.deselectAll();
+ that.update();
+ },
+ function(error) {that.success = false; that.failureReason = error;});
+ if(!that.success ) {
+ alert("Error:" + this.failureReason);
+ }
+ }
+}
+ };
+
+ AuthProviderUpdater.prototype.update = function()
+ {
+
+ var that = this;
+
+ xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"})
+ .then(function(data) {
+ that.authProviderData = data[0];
+ util.flattenStatistics( that.authProviderData );
+
+ that.usersGrid.update(that.authProviderData.users);
+
+ });
+
+
+ };
+
+ var addUser = {};
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ var convertToUser = function convertToUser(formValues) {
+ var newUser = {};
+ newUser.name = formValues.name;
+ for(var propName in formValues)
+ {
+ if(formValues.hasOwnProperty(propName)) {
+ if(formValues[ propName ] !== "") {
+ newUser[ propName ] = formValues[propName];
+ }
+ }
+ }
+
+ return newUser;
+ };
+
+
+ xhr.get({url: "authenticationprovider/addUser.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ node.innerHTML = data;
+ addUser.dialogNode = dom.byId("addUser");
+ parser.instantiate([addUser.dialogNode]);
+
+ var that = this;
+
+ theForm = registry.byId("formAddUser");
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ var newUser = convertToUser(theForm.getValues());
+
+
+ var url = "rest/user/"+encodeURIComponent(addUser.authProvider) +
+ "/"+encodeURIComponent(newUser.name);
+
+ xhr.put({url: url, sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newUser),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(that.success === true) {
+ registry.byId("addUser").hide();
+ } else {
+ alert("Error:" + that.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+ }});
+
+ addUser.show = function(authProvider) {
+ addUser.authProvider = authProvider;
+ registry.byId("formAddUser").reset();
+ registry.byId("addUser").show();
+ };
+
+
+ var setPassword = {};
+
+ var setPasswordNode = construct.create("div", null, win.body(), "last");
+
+ xhr.get({url: "authenticationprovider/setPassword.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ setPasswordNode.innerHTML = data;
+ setPassword.dialogNode = dom.byId("setPassword");
+ parser.instantiate([setPassword.dialogNode]);
+
+ var that = this;
+
+ theForm = registry.byId("formSetPassword");
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ var newUser = convertToUser(theForm.getValues());
+ newUser.name = setPassword.name;
+ newUser.id = setPassword.id;
+
+ var url = "rest/user/"+encodeURIComponent(setPassword.authProvider) +
+ "/"+encodeURIComponent(newUser.name);
+
+ xhr.put({url: url, sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ putData: json.toJson(newUser),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(that.success === true) {
+ registry.byId("setPassword").hide();
+ } else {
+ alert("Error:" + that.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+ }});
+
+ setPassword.show = function(authProvider, user) {
+ setPassword.authProvider = authProvider;
+ setPassword.name = user.name;
+ setPassword.id = user.id;
+ registry.byId("formSetPassword").reset();
+
+ var namebox = registry.byId("formSetPassword.name");
+ namebox.set("value", user.name);
+ namebox.set("disabled", true);
+
+ registry.byId("setPassword").show();
+
+ };
+
+
+
+ return DatabaseAuthManager;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js
new file mode 100644
index 0000000000..1aa05a5a3c
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js
@@ -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.
+ *
+ */
+define(["dojo/dom",
+ "dijit/registry",
+ "dijit/layout/ContentPane",
+ "qpid/management/Broker",
+ "qpid/management/VirtualHost",
+ "qpid/management/Exchange",
+ "qpid/management/Queue",
+ "qpid/management/Connection",
+ "qpid/management/AuthenticationProvider",
+ "dojo/ready",
+ "dojo/domReady!"],
+ function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, ready) {
+ var controller = {};
+
+ var constructors = { broker: Broker, virtualhost: VirtualHost, exchange: Exchange,
+ queue: Queue, connection: Connection, authenticationprovider: AuthProvider };
+
+ var tabDiv = dom.byId("managedViews");
+
+ ready(function() {
+ controller.tabContainer = registry.byId("managedViews");
+ });
+
+
+ controller.viewedObjects = {};
+
+ controller.show = function(objType, name, parent) {
+
+ function generateName(obj)
+ {
+ if(obj) {
+ var name = "";
+ if(obj.parent)
+ {
+ for(var prop in obj.parent) {
+ if(obj.parent.hasOwnProperty(prop)) {
+ name = name + generateName( obj.parent[ prop ]);
+ }
+ }
+
+ }
+ return name + parent.type +":" + parent.name + "/"
+ }
+ }
+
+ var that = this;
+ var objId = generateName(parent) + objType+":"+name;
+ if( this.viewedObjects[ objId ] ) {
+ this.tabContainer.selectChild(this.viewedObjects[ objId ].contentPane);
+ } else {
+ var Constructor = constructors[ objType ];
+ if(Constructor) {
+ var obj = new Constructor(name, parent, this);
+ this.viewedObjects[ objId ] = obj;
+
+ var contentPane = new ContentPane({ region: "center" ,
+ title: obj.getTitle(),
+ closable: true,
+ onClose: function() {
+ obj.close();
+ delete that.viewedObjects[ objId ];
+ return true;
+ }
+ });
+ this.tabContainer.addChild( contentPane );
+ obj.open(contentPane);
+ contentPane.startup();
+ if(obj.startup) {
+ obj.startup();
+ }
+ this.tabContainer.selectChild( contentPane );
+ }
+
+ }
+
+ };
+
+ ready(function() {
+ controller.show("broker","");
+ });
+
+
+ return controller;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/moveCopyMessages.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/moveCopyMessages.js
new file mode 100644
index 0000000000..8cc488324f
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/moveCopyMessages.js
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/dom-construct",
+ "dojo/_base/window",
+ "dijit/registry",
+ "dojo/parser",
+ "dojo/_base/array",
+ "dojo/_base/event",
+ 'dojo/_base/json',
+ "dojo/store/Memory",
+ "dijit/form/FilteringSelect",
+ "dojo/query",
+ "dojo/_base/connect",
+ "dojo/domReady!"],
+ function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, FilteringSelect, query, connect) {
+
+ var moveMessages = {};
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ xhr.get({url: "moveCopyMessages.html",
+ sync: true,
+ load: function(data) {
+ var theForm;
+ node.innerHTML = data;
+ moveMessages.dialogNode = dom.byId("moveMessages");
+ parser.instantiate([moveMessages.dialogNode]);
+
+ theForm = registry.byId("formMoveMessages");
+
+
+ var cancelButton = query(".moveMessageCancel")[0];
+ connect.connect(registry.byNode(cancelButton), "onClick",
+ function(evt){
+ event.stop(evt);
+ registry.byId("moveMessages").hide();
+ });
+
+
+ theForm.on("submit", function(e) {
+
+ event.stop(e);
+ if(theForm.validate()){
+
+ moveMessages.data.destinationQueue = theForm.getValues()["queue"];
+ var that = this;
+
+ xhr.post({url: "rest/message/"+encodeURIComponent(moveMessages.vhost)
+ +"/"+encodeURIComponent(moveMessages.queue),
+ sync: true, handleAs: "json",
+ headers: { "Content-Type": "application/json"},
+ postData: json.toJson(moveMessages.data),
+ load: function(x) {that.success = true; },
+ error: function(error) {that.success = false; that.failureReason = error;}});
+
+ if(this.success === true) {
+ registry.byId("moveMessages").hide();
+ if(moveMessages.next) {
+ moveMessages.next();
+ }
+ } else {
+ alert("Error:" + this.failureReason);
+ }
+
+ return false;
+
+
+ }else{
+ alert('Form contains invalid data. Please correct first');
+ return false;
+ }
+
+ });
+
+ }});
+
+ moveMessages.show = function(obj, next) {
+ var that = this;
+
+ moveMessages.vhost = obj.virtualhost;
+ moveMessages.queue = obj.queue;
+ moveMessages.data = obj.data;
+ moveMessages.next = next;
+ registry.byId("formMoveMessages").reset();
+
+
+
+ xhr.get({url: "rest/queue/" + encodeURIComponent(obj.virtualhost) + "?depth=0",
+ handleAs: "json"}).then(
+ function(data) {
+ var queues = [];
+ for(var i=0; i < data.length; i++) {
+ queues[i] = {id: data[i].name, name: data[i].name};
+ }
+ var queueStore = new Memory({ data: queues });
+
+
+ if(that.queueChooser) {
+ that.queueChooser.destroy( false );
+ }
+ var queueDiv = dom.byId("moveMessages.selectQueueDiv");
+ var input = construct.create("input", {id: "moveMessagesSelectQueue"}, queueDiv);
+
+ that.queueChooser = new FilteringSelect({ id: "moveMessagesSelectQueue",
+ name: "queue",
+ store: queueStore,
+ searchAttr: "name"}, input);
+
+
+
+ registry.byId("moveMessages").show();
+
+
+ });
+
+
+ };
+
+ return moveMessages;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js
new file mode 100644
index 0000000000..b1ccc0ca07
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+define(["dojo/_base/xhr",
+ "dojo/dom",
+ "dojo/dom-construct",
+ "dojo/dom-class",
+ "dojo/_base/window",
+ "dijit/registry",
+ "dojo/parser",
+ "dojo/_base/array",
+ "dojo/_base/event",
+ 'dojo/_base/json',
+ "dojo/query",
+ "dojo/_base/connect",
+ "qpid/common/properties",
+ "dojox/html/entities",
+ "dojo/domReady!"],
+ function (xhr, dom, construct, domClass, win, registry, parser, array, event, json, query, connect, properties, entities) {
+
+
+ function encode(val){
+ return typeof val === 'string' ? entities.encode(val) : val;
+ }
+
+ var showMessage = {};
+
+ showMessage.hide = function () {
+ if(this.populatedFields) {
+ for(var i = 0 ; i < this.populatedFields.length; i++) {
+ this.populatedFields[i].innerHTML = "";
+ }
+ this.populatedFields = [];
+ }
+ registry.byId("showMessage").hide();
+ };
+
+ showMessage.loadViewMessage = function(data) {
+ var that = this;
+ node.innerHTML = data;
+ showMessage.dialogNode = dom.byId("showMessage");
+ parser.instantiate([showMessage.dialogNode]);
+
+ var closeButton = query(".closeViewMessage")[0];
+ connect.connect(closeButton, "onclick",
+ function (evt) {
+ event.stop(evt);
+ showMessage.hide();
+ });
+ };
+
+ showMessage.populateShowMessage = function(data) {
+
+ this.populatedFields = [];
+
+ for(var attrName in data) {
+ if(data.hasOwnProperty(attrName)) {
+ var fields = query(".message-"+attrName, this.dialogNode);
+ if(fields && fields.length != 0) {
+ var field = fields[0];
+ this.populatedFields.push(field);
+ var val = data[attrName];
+ if(val) {
+ if(domClass.contains(field,"map")) {
+ var tableStr = "<table style='border: 1pt'><tr><th style='width: 6em; font-weight: bold'>Header</th><th style='font-weight: bold'>Value</th></tr>";
+ for(var name in val) {
+ if(val.hasOwnProperty(name)) {
+
+ tableStr += "<tr><td>"+encode(name)+"</td>";
+ tableStr += "<td>"+encode(val[ name ])+"</td></tr>";
+ }
+ field.innerHTML = tableStr;
+ }
+ tableStr += "</table>";
+ } else if(domClass.contains(field,"datetime")) {
+ var d = new Date(0);
+ d.setUTCSeconds(val/1000);
+ field.innerHTML = d.toLocaleString();
+ } else {
+ field.innerHTML = encode(val);
+ }
+ }
+ }
+ }
+ }
+ var contentField = query(".message-content", this.dialogNode)[0];
+
+ if(data.mimeType && data.mimeType.match(/text\/.*/)) {
+ xhr.get({url: "rest/message-content/" + encodeURIComponent(showMessage.virtualhost)
+ + "/" + encodeURIComponent(showMessage.queue)
+ + "/" + encodeURIComponent(showMessage.messageNumber),
+ sync: true
+
+ }).then(function(obj) { contentField.innerHTML = encode(obj) });
+ } else {
+ contentField.innerHTML = "<a href=\"" + "rest/message-content/" + encodeURIComponent(showMessage.virtualhost)
+ + "/" + encodeURIComponent(showMessage.queue)
+ + "/" + encodeURIComponent(showMessage.messageNumber)
+ + "\" target=\"_blank\">Download</a>";
+ }
+ this.populatedFields.push(contentField);
+
+ registry.byId("showMessage").show();
+ };
+
+ showMessage.show = function(obj) {
+ showMessage.virtualhost = obj.virtualhost;
+ showMessage.queue = obj.queue;
+ showMessage.messageNumber = obj.messageNumber;
+
+ xhr.get({url: "rest/message/" + encodeURIComponent(obj.virtualhost)
+ + "/" + encodeURIComponent(obj.queue)
+ + "/" + encodeURIComponent(obj.messageNumber),
+ sync: properties.useSyncGet,
+ handleAs: "json",
+ load: this.populateShowMessage
+ });
+ };
+
+ var node = construct.create("div", null, win.body(), "last");
+
+ xhr.get({url: "showMessage.html",
+ sync: true,
+ load: showMessage.loadViewMessage
+ });
+
+ return showMessage;
+ });
diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js
new file mode 100644
index 0000000000..b1d4abf8c1
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js
@@ -0,0 +1,313 @@
+/*
+ *
+ * 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.
+ *
+ */
+define(["dojo/_base/xhr",
+ "dojo/query",
+ "dojo/io-query",
+ "dijit/Tree",
+ "qpid/common/util",
+ "qpid/common/updater",
+ "qpid/management/controller",
+ "dojo/domReady!"],
+ function (xhr, query, ioQuery, Tree, util, updater, controller) {
+
+ function TreeViewModel(queryString) {
+ this.query = queryString;
+
+ this.onChildrenChange = function (parent, children) {
+ // fired when the set of children for an object change
+ };
+
+ this.onChange = function (object) {
+ // fired when the properties of an object change
+ };
+
+ this.onDelete = function (object) {
+ // fired when an object is deleted
+ };
+
+ }
+
+
+ TreeViewModel.prototype.buildModel = function (data) {
+ this.model = data;
+
+ };
+
+ TreeViewModel.prototype.updateModel = function (data) {
+ var that = this;
+
+ function checkForChanges(oldData, data) {
+ var propName;
+ if (oldData.name != data.name) {
+ that.onChange(data);
+ }
+
+ var childChanges = false;
+ // Iterate over old childTypes, check all are in new
+ for (propName in oldData) {
+ if (oldData.hasOwnProperty(propName)) {
+ var oldChildren = oldData[ propName ];
+ if (util.isArray(oldChildren)) {
+
+ var newChildren = data[ propName ];
+
+ if (!(newChildren && util.isArray(newChildren))) {
+ childChanges = true;
+ } else {
+ var subChanges = false;
+ // iterate over elements in array, make sure in both, in which case recurse
+ for (var i = 0; i < oldChildren.length; i++) {
+ var matched = false;
+ for (var j = 0; j < newChildren.length; j++) {
+ if (oldChildren[i].id == newChildren[j].id) {
+ checkForChanges(oldChildren[i], newChildren[j]);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ subChanges = true;
+ }
+ }
+ if (subChanges == true || oldChildren.length != newChildren.length) {
+ that.onChildrenChange({ id:data.id + propName, _dummyChild:propName, data:data },
+ newChildren);
+ }
+ }
+ }
+ }
+ }
+
+ for (propName in data) {
+ if (data.hasOwnProperty(propName)) {
+ var prop = data[ propName ];
+ if (util.isArray(prop)) {
+ if (!(oldData[ propName ] && util.isArray(oldData[propName]))) {
+ childChanges = true;
+ }
+ }
+ }
+ }
+
+ if (childChanges) {
+ var children = [];
+ that.getChildren(data, function (theChildren) {
+ children = theChildren
+ });
+ that.onChildrenChange(data, children);
+ }
+ }
+
+ var oldData = this.model;
+ this.model = data;
+
+ checkForChanges(oldData, data);
+ };
+
+
+ TreeViewModel.prototype.fetchItemByIdentity = function (id) {
+
+ function fetchItem(id, data) {
+ var propName;
+
+ if (data.id == id) {
+ return data;
+ } else if (id.indexOf(data.id) == 0) {
+ return { id:id, _dummyChild:id.substring(id.length), data:data };
+ } else {
+ for (propName in data) {
+ if (data.hasOwnProperty(propName)) {
+ var prop = data[ propName ];
+ if (util.isArray(prop)) {
+ for (var i = 0; i < prop.length; i++) {
+ var theItem = fetchItem(id, prop[i]);
+ if (theItem) {
+ return theItem;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ return fetchItem(id, this.model);
+ };
+
+ TreeViewModel.prototype.getChildren = function (parentItem, onComplete) {
+
+ if (parentItem) {
+ if (parentItem._dummyChild) {
+ onComplete(parentItem.data[ parentItem._dummyChild ]);
+ } else {
+ var children = [];
+ for (var propName in parentItem) {
+ if (parentItem.hasOwnProperty(propName)) {
+ var prop = parentItem[ propName ];
+
+ if (util.isArray(prop)) {
+ children.push({ id:parentItem.id
+ + propName, _dummyChild:propName, data:parentItem });
+ }
+ }
+ }
+ onComplete(children);
+ }
+ } else {
+ onComplete([]);
+ }
+ };
+
+ TreeViewModel.prototype.getIdentity = function (theItem) {
+ if (theItem) {
+ return theItem.id;
+ }
+
+ };
+
+ TreeViewModel.prototype.getLabel = function (theItem) {
+ if (theItem) {
+ if (theItem._dummyChild) {
+ return theItem._dummyChild;
+ } else {
+ return theItem.name;
+ }
+ } else {
+ return "";
+ }
+ };
+
+ TreeViewModel.prototype.getRoot = function (onItem) {
+ onItem(this.model);
+ };
+
+ TreeViewModel.prototype.mayHaveChildren = function (theItem) {
+ if (theItem) {
+ if (theItem._dummyChild) {
+ return true;
+ } else {
+ for (var propName in theItem) {
+ if (theItem.hasOwnProperty(propName)) {
+ var prop = theItem[ propName ];
+ if (util.isArray(prop)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ } else {
+ return false;
+ }
+ };
+
+ TreeViewModel.prototype.relocate = function (theItem) {
+
+ function findItemDetails(theItem, details, type, object) {
+ if (theItem.id == object.id) {
+ details.type = type;
+ details[ type ] = object.name;
+ } else {
+ details[ type ] = object.name;
+
+ // iterate over children
+ for (var propName in object) {
+ if (object.hasOwnProperty(propName)) {
+ var prop = object[ propName ];
+ if (util.isArray(prop)) {
+ for (var i = 0; i < prop.length; i++) {
+ findItemDetails(theItem, details, propName.substring(0, propName.length - 1),
+ prop[i]);
+
+ if (details.type) {
+ break;
+ }
+ }
+ }
+ if (details.type) {
+ break;
+ }
+ }
+ }
+
+ if (!details.type) {
+ details[ type ] = null;
+ }
+ }
+ }
+
+ var details = new Object();
+
+ findItemDetails(theItem, details, "broker", this.model);
+
+ if (details.type == "broker") {
+ controller.show("broker", "");
+ } else if (details.type == "virtualhost") {
+ controller.show("virtualhost", details.virtualhost, {type:"broker", name:""});
+ } else if (details.type == "exchange") {
+ controller.show("exchange", details.exchange, { type: "virtualhost", name: details.virtualhost, parent: {broker: {type:"broker", name:""}}});
+ } else if (details.type == "queue") {
+ controller.show("queue", details.queue, { type: "virtualhost", name: details.virtualhost, parent: {broker: {type:"broker", name:""}}});
+ } else if (details.type == "connection") {
+ controller.show("connection", details.connection, { type: "virtualhost", name: details.virtualhost, parent: {broker: {type:"broker", name:""}}});
+ } else if (details.type == 'port') {
+ controller.show("port", details.port, { type: "virtualhost", name: details.virtualhost, parent: {broker: {type:"broker", name:""}}});
+ } else if (details.type == 'authenticationprovider') {
+ controller.show("authenticationprovider", details.authenticationprovider, {broker: {type:"broker", name:""}});
+ }
+
+
+
+ };
+
+ TreeViewModel.prototype.update = function () {
+ var thisObj = this;
+
+ xhr.get({url:this.query, sync: true, handleAs:"json"})
+ .then(function (data) {
+ if (thisObj.model) {
+ thisObj.updateModel(data);
+ }
+ else {
+ thisObj.buildModel(data);
+ }
+ });
+
+ };
+
+ query('div[qpid-type="treeView"]').forEach(function(node, index, arr) {
+ var treeModel = new TreeViewModel("rest/structure");
+ treeModel.update();
+ var tree = new Tree({ model: treeModel }, node);
+ tree.on("dblclick",
+ function (object) {
+ if (object && !object._dummyChild) {
+ treeModel.relocate(object);
+ }
+
+ }, true);
+ tree.startup();
+ updater.add( treeModel );
+ });
+
+ return TreeViewModel;
+ }); \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/management.html b/java/broker-plugins/management-http/src/main/java/resources/management.html
new file mode 100644
index 0000000000..a8345a8503
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/management.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<!--
+ ~ 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.
+ -->
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Qpid Management</title>
+ <link rel="stylesheet" href="dojo/dojo/resources/dojo.css">
+ <link rel="stylesheet" href="dojo/dijit/themes/claro/claro.css">
+ <link rel="stylesheet" href="dojo/dojox/grid/resources/claroGrid.css">
+ <link rel="stylesheet" href="dojo/dojox/grid/enhanced/resources/claro/EnhancedGrid.css">
+ <link rel="stylesheet" href="dojo/dojox/grid/enhanced/resources/EnhancedGrid_rtl.css">
+ <link rel="stylesheet" href="css/common.css" media="screen">
+ <script>
+ function getContextPath()
+ {
+ var contextPath = "/";
+ var documentURL = document.URL;
+ var managementPageStart = documentURL.lastIndexOf("/");
+ var firstSlashPos = documentURL.indexOf("/", documentURL.indexOf("//") + 2);
+ if (managementPageStart > firstSlashPos)
+ {
+ contextPath = documentURL.substring(firstSlashPos, managementPageStart);
+ }
+ return contextPath;
+ }
+
+ var dojoConfig = {
+ tlmSiblingOfDojo:false,
+ parseOnLoad:true,
+ async:true,
+ baseUrl: getContextPath(),
+ packages:[
+ { name:"dojo", location:"dojo/dojo" },
+ { name:"dijit", location:"dojo/dijit" },
+ { name:"dojox", location:"dojo/dojox" },
+ { name:"qpid", location:"js/qpid" }
+ ]
+ };
+
+ </script>
+ <script src="dojo/dojo/dojo.js">
+ </script>
+
+ <script>
+ require(["dijit/layout/BorderContainer",
+ "dijit/layout/TabContainer",
+ "dijit/layout/ContentPane",
+ "dijit/TitlePane",
+ "dojo/parser",
+ "qpid/management/treeView",
+ "qpid/management/controller",
+ "qpid/common/footer",
+ "qpid/authorization/sasl"]);
+ </script>
+
+</head>
+<body class="claro">
+
+<div id="pageLayout" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'headline', gutters: false">
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
+ <div id="header" class="header"></div>
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
+ <div id="login"></div>
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'leading', splitter: true">
+ <div qpid-type="treeView" qpid-props="query: 'rest/structure'" ></div>
+ </div>
+ <div id="managedViews" data-dojo-type="dijit.layout.TabContainer" data-dojo-props="region:'center', tabPosition: 'top'">
+ </div>
+ <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
+ <div qpid-type="footer"></div>
+ </div>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/moveCopyMessages.html b/java/broker-plugins/management-http/src/main/java/resources/moveCopyMessages.html
new file mode 100644
index 0000000000..f188c3001c
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/moveCopyMessages.html
@@ -0,0 +1,36 @@
+<!--
+ ~ 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.
+ -->
+
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Move/Copy Messages'" id="moveMessages">
+ <form id="formMoveMessages" method="post" dojoType="dijit.form.Form">
+ <table cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top"><strong>Queue: </strong></td>
+ <td><div id="moveMessages.selectQueueDiv"></div></td>
+ </tr>
+ </table>
+ <br/>
+
+ <!-- submit buttons -->
+
+ <input type="button" value="Cancel" label="Cancel" dojoType="dijit.form.Button" class="moveMessageCancel"/>
+ <input type="submit" value="Move Messages" label="Move Messages" dojoType="dijit.form.Button" />
+
+ </form>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html b/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html
new file mode 100644
index 0000000000..c5d4e48a75
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html
@@ -0,0 +1,25 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="authorizationProvider">
+ <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">Type:</span><span class="type" style="position:absolute; left:6em"></span>
+</div> \ No newline at end of file
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showBroker.html b/java/broker-plugins/management-http/src/main/java/resources/showBroker.html
new file mode 100644
index 0000000000..a39e334c40
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showBroker.html
@@ -0,0 +1,25 @@
+<div class="broker">
+ <span>Name:</span><span class="broker-name" style="position:absolute; left:6em"></span>
+ <br/>
+<!-- <span>State:</span><span class="broker-state" style="position:absolute; left:6em"></span>
+ <br/>
+ <span>Durable:</span><span class="broker-durable" style="position:absolute; left:6em"></span>
+ <br/>
+ <span>Lifespan:</span><span class="broker-lifetimePolicy" style="position:absolute; left:6em" ></span>
+ <br/> -->
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Virtual Hosts'">
+ <div class="broker-virtualhosts"></div>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Ports'">
+ <div class="broker-ports"></div>
+ </div>
+ <br/>
+
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Log File', open: false">
+ <div class="broker-logfile"></div>
+ </div>
+ <br/>
+</div>
+
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showConnection.html b/java/broker-plugins/management-http/src/main/java/resources/showConnection.html
new file mode 100644
index 0000000000..84854daf47
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showConnection.html
@@ -0,0 +1,47 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="connection">
+ <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">State:</span><span class="state" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Pre-fetched:</span>
+ <br/>
+ <span style="">Durable:</span><span class="durable" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Inbound:</span>
+ <span class="msgInRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesInRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesInRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">Lifespan:</span><span style="position:absolute; left:6em" class="lifetimePolicy"></span>
+ <span style="position:absolute; left:26em">Outbound:</span>
+ <span class="msgOutRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesOutRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesOutRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Sessions'">
+ <div class="sessions"></div>
+ </div>
+ <br/>
+
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showExchange.html b/java/broker-plugins/management-http/src/main/java/resources/showExchange.html
new file mode 100644
index 0000000000..f33b029026
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showExchange.html
@@ -0,0 +1,50 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="exchange">
+ <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">State:</span><span class="state" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">Durable:</span><span class="durable" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Inbound:</span>
+ <span class="msgInRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesInRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesInRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">Lifespan:</span><span style="position:absolute; left:6em" class="lifetimePolicy"></span>
+ <span style="position:absolute; left:26em">Dropped:</span>
+ <span class="msgDropRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesDropRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesDropRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Bindings'">
+ <div class="bindings"></div>
+ <button data-dojo-type="dijit.form.Button" class="addBindingButton">Add Binding</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteBindingButton">Delete Binding</button>
+ </div>
+ <br/>
+ <div class="dijitDialogPaneActionBar">
+ <button data-dojo-type="dijit.form.Button" class="deleteExchangeButton" type="button">Delete Exchange</button>
+ </div>
+</div>
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showMessage.html b/java/broker-plugins/management-http/src/main/java/resources/showMessage.html
new file mode 100644
index 0000000000..0dea508c60
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showMessage.html
@@ -0,0 +1,73 @@
+<!--
+ ~ 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.
+ -->
+
+<div class="dijitHidden">
+ <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'View Message'" id="showMessage">
+
+ <table style="border: 0;">
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Message Number:</span></td>
+ <td><span class="message-id"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Message Id:</span></td>
+ <td><span class="message-messageId"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">State:</span></td>
+ <td><span class="message-state"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Persistent:</span></td>
+ <td><span class="message-persistent boolean"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Priority:</span></td>
+ <td><span class="message-priority"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Arrival Time:</span>
+ </td><td><span class="message-arrivalTime datetime"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Expiration:</span></td>
+ <td><span class="message-expiration datetime"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">MIME Type:</span></td>
+ <td><span class="message-mimeType"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">User:</span></td>
+ <td><span class="message-userId"></span></td>
+ </tr>
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Headers:</span></td>
+ <td><div class="message-headers map"></div></td>
+ </tr>
+
+ <tr style="margin-bottom: 4pt">
+ <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Content:</span></td>
+ <td><div class="message-content"></div></td>
+ </tr>
+ </table>
+ <br/>
+ <input type="button" value="Close" label="Close" dojoType="dijit.form.Button" class="closeViewMessage"/>
+
+ </div>
+</div>
+
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showQueue.html b/java/broker-plugins/management-http/src/main/java/resources/showQueue.html
new file mode 100644
index 0000000000..929e1d1f23
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showQueue.html
@@ -0,0 +1,103 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<div class="queue">
+ <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Size:</span>
+ <span class="queueDepthMessages" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msgs</span>
+ <span class="queueDepthBytes" style="position:absolute; right: 3.3em">(</span>
+ <span class="queueDepthBytesUnits" style="position:absolute; right: 0em; width: 3em">)</span>
+ <br/>
+ <span style="">State:</span><span class="state" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Pre-fetched:</span>
+ <span class="unacknowledgedMessages" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msgs</span>
+ <span class="unacknowledgedBytes" style="position:absolute; right: 3.3em"></span>
+ <span class="unacknowledgedBytesUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">Durable:</span><span class="durable" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Inbound:</span>
+ <span class="msgInRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesInRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesInRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">Lifespan:</span><span style="position:absolute; left:6em" class="lifetimePolicy"></span>
+ <span style="position:absolute; left:26em">Outbound:</span>
+ <span class="msgOutRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesOutRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesOutRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">AlternateExchange:</span><span class="alternateExchange" style="position:absolute; left:10em"></span>
+ <span style="position:absolute; left:26em">Type:</span><span style="position:absolute; left:29em" class="type"></span>
+ <span style="position:absolute; right:1em" class="typeQualifier"></span>
+ <br/>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Bindings'">
+ <div class="bindings"></div>
+ <button data-dojo-type="dijit.form.Button" class="addBindingButton" type="button">Add Binding</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Consumers'">
+ <div class="consumers"></div>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Messages'">
+ <div class="messages"></div>
+ <button data-dojo-type="dijit.form.Button" class="deleteMessagesButton" type="button">Delete Messages</button>
+ <button data-dojo-type="dijit.form.Button" class="moveMessagesButton" type="button">Move Messages</button>
+ <button data-dojo-type="dijit.form.Button" class="copyMessagesButton" type="button">Copy Messages</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Alerting Thresholds', open: false">
+ <span style="">Queue Depth:</span>
+ <span class="alertThresholdQueueDepthMessages"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span style="position:absolute; left:12.2em">msgs</span>
+
+ <span style="position:absolute; left:21em">Queue Depth:</span>
+ <span class="alertThresholdQueueDepthBytes"
+ style="position:absolute; left:22em; width:8em; text-align:right"></span>
+ <span class="alertThresholdQueueDepthBytesUnits" style="position:absolute; left:30.2em"></span>
+ <br>
+ <span style="">Message Age:</span>
+ <span class="alertThresholdMessageAge"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span class="alertThresholdMessageAgeUnits" style="position:absolute; left:12.2em"></span>
+
+ <span style="position:absolute; left:21em">Message Size: </span>
+ <span class="alertThresholdMessageSize"
+ style="position:absolute; left:25em; width:5em; text-align:right"></span>
+ <span class="alertThresholdMessageSizeUnits" style="position:absolute; left:30.2em"></span>
+ <br/>
+ <br/>
+ <span style="">Alert frequency:</span>
+ <span class="alertRepeatGap"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span class="alertRepeatGapUnits" style="position:absolute; left:12.2em"></span>
+ </div>
+
+ <div class="dijitDialogPaneActionBar">
+ <button data-dojo-type="dijit.form.Button" class="deleteQueueButton" type="button">Delete Queue</button>
+ </div>
+</div>
+
diff --git a/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html b/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html
new file mode 100644
index 0000000000..73c912e0d4
--- /dev/null
+++ b/java/broker-plugins/management-http/src/main/java/resources/showVirtualHost.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<!--
+ -
+ - 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.
+ -
+ -->
+
+<div class="virtualhost">
+ <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">State:</span><span class="state" style="position:absolute; left:6em"></span>
+ <br/>
+ <span style="">Durable:</span><span class="durable" style="position:absolute; left:6em"></span>
+ <span style="position:absolute; left:26em">Inbound:</span>
+ <span class="msgInRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesInRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesInRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <span style="">Lifespan:</span><span style="position:absolute; left:6em" class="lifetimePolicy"></span>
+ <span style="position:absolute; left:26em">Outbound:</span>
+ <span class="msgOutRate" style="position:absolute; right:9.5em"></span>
+ <span style="position:absolute; right: 5em; width: 4em"> msg/s</span>
+ <span class="bytesOutRate" style="position:absolute; right: 3.3em"></span>
+ <span class="bytesOutRateUnits" style="position:absolute; right: 0em; width: 3em"></span>
+ <br/>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Exchanges'">
+ <div class="exchanges"></div>
+ <button data-dojo-type="dijit.form.Button" class="addExchangeButton">Add Exchange</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteExchangeButton">Delete Exchange</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Queues'">
+ <div class="queues"></div>
+ <button data-dojo-type="dijit.form.Button" class="addQueueButton">Add Queue</button>
+ <button data-dojo-type="dijit.form.Button" class="deleteQueueButton">Delete Queue</button>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Connections'">
+ <div class="connections"></div>
+ </div>
+ <br/>
+ <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Alerting Thresholds', open: false">
+ <span style="">Queue Depth:</span>
+ <span class="alertThresholdQueueDepthMessages"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span style="position:absolute; left:12.2em">msgs</span>
+
+ <span style="position:absolute; left:21em">Queue Depth:</span>
+ <span class="alertThresholdQueueDepthBytes"
+ style="position:absolute; left:22em; width:8em; text-align:right"></span>
+ <span class="alertThresholdQueueDepthBytesUnits" style="position:absolute; left:30.2em"></span>
+ <br>
+ <span style="">Message Age:</span>
+ <span class="alertThresholdMessageAge"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span class="alertThresholdMessageAgeUnits" style="position:absolute; left:12.2em"></span>
+
+ <span style="position:absolute; left:21em">Message Size: </span>
+ <span class="alertThresholdMessageSize"
+ style="position:absolute; left:25em; width:5em; text-align:right"></span>
+ <span class="alertThresholdMessageSizeUnits" style="position:absolute; left:30.2em"></span>
+ <br/>
+ <br/>
+ <span style="">Alert frequency:</span>
+ <span class="alertRepeatGap"
+ style="position:absolute; left:4em; width:8em; text-align:right"></span>
+ <span class="alertRepeatGapUnits" style="position:absolute; left:12.2em"></span>
+ </div>
+</div>
+
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/Asserts.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/Asserts.java
new file mode 100644
index 0000000000..2595007574
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/Asserts.java
@@ -0,0 +1,249 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.jms.JMSException;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+
+public class Asserts
+{
+ public static final String STATISTICS_ATTRIBUTE = "statistics";
+
+ public static void assertVirtualHost(String virtualHostName, Map<String, Object> virtualHost)
+ {
+ assertNotNull("Virtualhost " + virtualHostName + " data are not found", virtualHost);
+ assertAttributesPresent(virtualHost, VirtualHost.AVAILABLE_ATTRIBUTES, VirtualHost.TIME_TO_LIVE,
+ VirtualHost.CREATED, VirtualHost.UPDATED, VirtualHost.SUPPORTED_QUEUE_TYPES, VirtualHost.STORE_CONFIGURATION);
+
+ assertEquals("Unexpected value of attribute " + VirtualHost.NAME, virtualHostName, virtualHost.get(VirtualHost.NAME));
+ assertNotNull("Unexpected value of attribute " + VirtualHost.ID, virtualHost.get(VirtualHost.ID));
+ assertEquals("Unexpected value of attribute " + VirtualHost.STATE, State.ACTIVE.name(),
+ virtualHost.get(VirtualHost.STATE));
+ assertEquals("Unexpected value of attribute " + VirtualHost.DURABLE, Boolean.TRUE,
+ virtualHost.get(VirtualHost.DURABLE));
+ assertEquals("Unexpected value of attribute " + VirtualHost.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ virtualHost.get(VirtualHost.LIFETIME_POLICY));
+ assertEquals("Unexpected value of attribute " + VirtualHost.DEAD_LETTER_QUEUE_ENABLED, Boolean.FALSE,
+ virtualHost.get(VirtualHost.DEAD_LETTER_QUEUE_ENABLED));
+
+ @SuppressWarnings("unchecked")
+ Collection<String> exchangeTypes = (Collection<String>) virtualHost.get(VirtualHost.SUPPORTED_EXCHANGE_TYPES);
+ assertEquals("Unexpected value of attribute " + VirtualHost.SUPPORTED_EXCHANGE_TYPES,
+ new HashSet<String>(Arrays.asList("headers", "topic", "direct", "fanout", "management")),
+ new HashSet<String>(exchangeTypes));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) virtualHost.get(STATISTICS_ATTRIBUTE);
+ Asserts.assertAttributesPresent(statistics, VirtualHost.AVAILABLE_STATISTICS, VirtualHost.BYTES_RETAINED,
+ VirtualHost.LOCAL_TRANSACTION_BEGINS, VirtualHost.LOCAL_TRANSACTION_ROLLBACKS,
+ VirtualHost.MESSAGES_RETAINED, VirtualHost.STATE_CHANGED, VirtualHost.XA_TRANSACTION_BRANCH_ENDS,
+ VirtualHost.XA_TRANSACTION_BRANCH_STARTS, VirtualHost.XA_TRANSACTION_BRANCH_SUSPENDS);
+
+ }
+
+ public static void assertQueue(String queueName, String queueType, Map<String, Object> queueData)
+ {
+ assertQueue(queueName, queueType, queueData, null);
+ }
+
+ public static void assertQueue(String queueName, String queueType, Map<String, Object> queueData, Map<String, Object> expectedAttributes)
+ {
+ assertNotNull("Queue " + queueName + " is not found!", queueData);
+ Asserts.assertAttributesPresent(queueData, Queue.AVAILABLE_ATTRIBUTES, Queue.CREATED, Queue.UPDATED,
+ Queue.DESCRIPTION, Queue.TIME_TO_LIVE, Queue.ALTERNATE_EXCHANGE, Queue.OWNER, Queue.NO_LOCAL, Queue.LVQ_KEY,
+ Queue.SORT_KEY, Queue.MESSAGE_GROUP_KEY, Queue.MESSAGE_GROUP_DEFAULT_GROUP,
+ Queue.MESSAGE_GROUP_SHARED_GROUPS, Queue.PRIORITIES);
+
+ assertEquals("Unexpected value of queue attribute " + Queue.NAME, queueName, queueData.get(Queue.NAME));
+ assertNotNull("Unexpected value of queue attribute " + Queue.ID, queueData.get(Queue.ID));
+ assertEquals("Unexpected value of queue attribute " + Queue.STATE, State.ACTIVE.name(), queueData.get(Queue.STATE));
+ assertEquals("Unexpected value of queue attribute " + Queue.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ queueData.get(Queue.LIFETIME_POLICY));
+ assertEquals("Unexpected value of queue attribute " + Queue.TYPE, queueType, queueData.get(Queue.TYPE));
+ if (expectedAttributes == null)
+ {
+ assertEquals("Unexpected value of queue attribute " + Queue.EXCLUSIVE, Boolean.FALSE, queueData.get(Queue.EXCLUSIVE));
+ assertEquals("Unexpected value of queue attribute " + Queue.MAXIMUM_DELIVERY_ATTEMPTS, 0,
+ queueData.get(Queue.MAXIMUM_DELIVERY_ATTEMPTS));
+ assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 0,
+ queueData.get(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES));
+ assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 0,
+ queueData.get(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES));
+ assertEquals("Unexpected value of queue attribute " + Queue.QUEUE_FLOW_STOPPED, Boolean.FALSE,
+ queueData.get(Queue.QUEUE_FLOW_STOPPED));
+ }
+ else
+ {
+ for (Map.Entry<String, Object> attribute : expectedAttributes.entrySet())
+ {
+ assertEquals("Unexpected value of " + queueName + " queue attribute " + attribute.getKey(),
+ attribute.getValue(), queueData.get(attribute.getKey()));
+ }
+ }
+
+ assertNotNull("Unexpected value of queue attribute statistics", queueData.get(Asserts.STATISTICS_ATTRIBUTE));
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) queueData.get(Asserts.STATISTICS_ATTRIBUTE);
+ Asserts.assertAttributesPresent(statistics, Queue.AVAILABLE_STATISTICS, Queue.DISCARDS_TTL_BYTES,
+ Queue.DISCARDS_TTL_MESSAGES, Queue.STATE_CHANGED);
+ }
+
+ public static void assertAttributesPresent(Map<String, Object> data, String[] attributes)
+ {
+ for (String name : attributes)
+ {
+ assertNotNull("Attribute " + name + " is not present", data.get(name));
+ }
+ }
+
+ public static void assertAttributesPresent(Map<String, Object> data, Collection<String> attributes,
+ String... unsupportedAttributes)
+ {
+ for (String name : attributes)
+ {
+ boolean unsupported = false;
+ for (String unsupportedAttribute : unsupportedAttributes)
+ {
+ if (unsupportedAttribute.equals(name))
+ {
+ unsupported = true;
+ break;
+ }
+ }
+ if (unsupported)
+ {
+ continue;
+ }
+ assertNotNull("Attribute " + name + " is not present", data.get(name));
+ }
+ }
+
+ public static void assertConnection(Map<String, Object> connectionData, AMQConnection connection) throws JMSException
+ {
+ assertNotNull("Unexpected connection data", connectionData);
+ assertAttributesPresent(connectionData, Connection.AVAILABLE_ATTRIBUTES, Connection.STATE, Connection.DURABLE,
+ Connection.LIFETIME_POLICY, Connection.TIME_TO_LIVE, Connection.CREATED, Connection.UPDATED,
+ Connection.INCOMING, Connection.REMOTE_PROCESS_NAME, Connection.REMOTE_PROCESS_PID,
+ Connection.LOCAL_ADDRESS, Connection.PROPERTIES);
+
+ assertEquals("Unexpected value of connection attribute " + Connection.SESSION_COUNT_LIMIT,
+ (int) connection.getMaximumChannelCount(), connectionData.get(Connection.SESSION_COUNT_LIMIT));
+ assertEquals("Unexpected value of connection attribute " + Connection.CLIENT_ID, "clientid",
+ connectionData.get(Connection.CLIENT_ID));
+ assertEquals("Unexpected value of connection attribute " + Connection.PRINCIPAL, "guest",
+ connectionData.get(Connection.PRINCIPAL));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) connectionData.get(STATISTICS_ATTRIBUTE);
+ assertAttributesPresent(statistics, Connection.AVAILABLE_STATISTICS, Connection.LOCAL_TRANSACTION_BEGINS,
+ Connection.LOCAL_TRANSACTION_ROLLBACKS, Connection.STATE_CHANGED, Connection.XA_TRANSACTION_BRANCH_ENDS,
+ Connection.XA_TRANSACTION_BRANCH_STARTS, Connection.XA_TRANSACTION_BRANCH_SUSPENDS);
+ assertEquals("Unexpected value of connection statistics attribute " + Connection.SESSION_COUNT, 1,
+ statistics.get(Connection.SESSION_COUNT));
+ }
+
+ public static void assertPortAttributes(Map<String, Object> port)
+ {
+ assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED);
+
+ assertNotNull("Unexpected value of attribute " + Port.ID, port.get(Port.ID));
+ assertEquals("Unexpected value of attribute " + Port.DURABLE, Boolean.FALSE, port.get(Port.DURABLE));
+ assertEquals("Unexpected value of attribute " + Port.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ port.get(Broker.LIFETIME_POLICY));
+ assertEquals("Unexpected value of attribute " + Port.STATE, State.ACTIVE.name(), port.get(Port.STATE));
+ assertEquals("Unexpected value of attribute " + Port.TIME_TO_LIVE, 0, port.get(Port.TIME_TO_LIVE));
+ assertNotNull("Unexpected value of attribute " + Port.BINDING_ADDRESS, port.get(Port.BINDING_ADDRESS));
+ assertNotNull("Unexpected value of attribute " + Port.PROTOCOLS, port.get(Port.PROTOCOLS));
+ assertNotNull("Unexpected value of attribute " + Port.NAME, port.get(Port.NAME));
+
+ @SuppressWarnings("unchecked")
+ Collection<String> transports = (Collection<String>) port.get(Port.TRANSPORTS);
+ assertEquals("Unexpected value of attribute " + Port.TRANSPORTS, new HashSet<String>(Arrays.asList("TCP")),
+ new HashSet<String>(transports));
+ }
+
+ public static void assertDurableExchange(String exchangeName, String type, Map<String, Object> exchangeData)
+ {
+ assertExchange(exchangeName, type, exchangeData);
+
+ assertEquals("Unexpected value of exchange attribute " + Exchange.DURABLE, Boolean.TRUE,
+ exchangeData.get(Exchange.DURABLE));
+ }
+
+ public static void assertExchange(String exchangeName, String type, Map<String, Object> exchangeData)
+ {
+ assertNotNull("Exchange " + exchangeName + " is not found!", exchangeData);
+ assertAttributesPresent(exchangeData, Exchange.AVAILABLE_ATTRIBUTES, Exchange.CREATED, Exchange.UPDATED,
+ Exchange.ALTERNATE_EXCHANGE, Exchange.TIME_TO_LIVE);
+
+ assertEquals("Unexpected value of exchange attribute " + Exchange.NAME, exchangeName,
+ exchangeData.get(Exchange.NAME));
+ assertNotNull("Unexpected value of exchange attribute " + Exchange.ID, exchangeData.get(VirtualHost.ID));
+ assertEquals("Unexpected value of exchange attribute " + Exchange.STATE, State.ACTIVE.name(),
+ exchangeData.get(Exchange.STATE));
+
+ assertEquals("Unexpected value of exchange attribute " + Exchange.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ exchangeData.get(Exchange.LIFETIME_POLICY));
+ assertEquals("Unexpected value of exchange attribute " + Exchange.TYPE, type, exchangeData.get(Exchange.TYPE));
+ assertNotNull("Unexpected value of exchange attribute statistics", exchangeData.get(STATISTICS_ATTRIBUTE));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) exchangeData.get(STATISTICS_ATTRIBUTE);
+ assertAttributesPresent(statistics, Exchange.AVAILABLE_STATISTICS, Exchange.STATE_CHANGED, Exchange.PRODUCER_COUNT);
+ }
+
+ public static void assertBinding(String bindingName, String queueName, String exchange, Map<String, Object> binding)
+ {
+ assertNotNull("Binding map should not be null", binding);
+ assertAttributesPresent(binding, Binding.AVAILABLE_ATTRIBUTES, Binding.STATE, Binding.TIME_TO_LIVE,
+ Binding.CREATED, Binding.UPDATED);
+
+ assertEquals("Unexpected binding attribute " + Binding.NAME, bindingName, binding.get(Binding.NAME));
+ assertEquals("Unexpected binding attribute " + Binding.QUEUE, queueName, binding.get(Binding.QUEUE));
+ assertEquals("Unexpected binding attribute " + Binding.EXCHANGE, exchange, binding.get(Binding.EXCHANGE));
+ assertEquals("Unexpected binding attribute " + Binding.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ binding.get(Binding.LIFETIME_POLICY));
+ }
+
+ public static void assertBinding(String queueName, String exchange, Map<String, Object> binding)
+ {
+ assertBinding(queueName, queueName, exchange, binding);
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java
new file mode 100644
index 0000000000..37bc2733b0
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java
@@ -0,0 +1,53 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.User;
+
+public class AuthenticationProviderRestTest extends QpidRestTestCase
+{
+
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> providerDetails = getJsonAsList("/rest/authenticationprovider");
+ assertNotNull("Providers details cannot be null", providerDetails);
+ assertEquals("Unexpected number of providers", 1, providerDetails.size());
+ for (Map<String, Object> provider : providerDetails)
+ {
+ assertProvider("PrincipalDatabaseAuthenticationManager", provider);
+ Map<String, Object> data = getJsonAsSingletonList("/rest/authenticationprovider/"
+ + provider.get(AuthenticationProvider.NAME));
+ assertNotNull("Cannot load data for " + provider.get(AuthenticationProvider.NAME), data);
+ assertProvider("PrincipalDatabaseAuthenticationManager", data);
+ }
+ }
+
+ private void assertProvider(String type, Map<String, Object> provider)
+ {
+ Asserts.assertAttributesPresent(provider, AuthenticationProvider.AVAILABLE_ATTRIBUTES,
+ AuthenticationProvider.CREATED, AuthenticationProvider.UPDATED, AuthenticationProvider.DESCRIPTION,
+ AuthenticationProvider.TIME_TO_LIVE);
+ assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.STATE, State.ACTIVE.name(),
+ provider.get(AuthenticationProvider.STATE));
+ assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.LIFETIME_POLICY,
+ LifetimePolicy.PERMANENT.name(), provider.get(AuthenticationProvider.LIFETIME_POLICY));
+ assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.DURABLE, Boolean.TRUE,
+ provider.get(AuthenticationProvider.DURABLE));
+ assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.TYPE, type,
+ provider.get(AuthenticationProvider.TYPE));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> users = (List<Map<String, Object>>) provider.get("users");
+ assertNotNull("Users are not found", users);
+ assertTrue("Unexpected number of users", users.size() > 1);
+ for (Map<String, Object> user : users)
+ {
+ assertNotNull("Attribute " + User.ID, user.get(User.ID));
+ assertNotNull("Attribute " + User.NAME, user.get(User.NAME));
+ }
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java
new file mode 100644
index 0000000000..527eb16927
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java
@@ -0,0 +1,109 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.Binding;
+
+public class BindingRestTest extends QpidRestTestCase
+{
+
+ public void testGetAllBindings() throws Exception
+ {
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding");
+ assertNotNull("Bindings cannot be null", bindings);
+ assertTrue("Unexpected number of bindings", bindings.size() >= EXPECTED_HOSTS.length * EXPECTED_QUEUES.length);
+ for (Map<String, Object> binding : bindings)
+ {
+ Asserts.assertBinding((String) binding.get(Binding.NAME), (String) binding.get(Binding.EXCHANGE), binding);
+ }
+ }
+
+ public void testGetVirtualHostBindings() throws Exception
+ {
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding/test");
+ assertNotNull("Bindings cannot be null", bindings);
+ assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length * 2, bindings.size());
+ for (String queueName : EXPECTED_QUEUES)
+ {
+ Map<String, Object> searchAttributes = new HashMap<String, Object>();
+ searchAttributes.put(Binding.NAME, queueName);
+ searchAttributes.put(Binding.EXCHANGE, "amq.direct");
+
+ Map<String, Object> binding = find(searchAttributes, bindings);
+ Asserts.assertBinding(queueName, "amq.direct", binding);
+
+ searchAttributes.put(Binding.EXCHANGE, "<<default>>");
+
+ binding = find(searchAttributes, bindings);
+ Asserts.assertBinding(queueName, "<<default>>", binding);
+ }
+ }
+
+ public void testGetVirtualHostExchangeBindings() throws Exception
+ {
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding/test/amq.direct");
+ assertNotNull("Bindings cannot be null", bindings);
+ assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length, bindings.size());
+ for (String queueName : EXPECTED_QUEUES)
+ {
+ Map<String, Object> binding = find(Binding.NAME, queueName, bindings);
+ Asserts.assertBinding(queueName, "amq.direct", binding);
+ }
+ }
+
+ public void testGetVirtualHostExchangeQueueBindings() throws Exception
+ {
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue");
+ assertNotNull("Bindings cannot be null", bindings);
+ assertEquals("Unexpected number of bindings", 1, bindings.size());
+ Asserts.assertBinding("queue", "amq.direct", bindings.get(0));
+ }
+
+
+ public void testDeleteBinding() throws Exception
+ {
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue/queue");
+ assertEquals("Unexpected number of bindings", 1, bindings.size());
+ Asserts.assertBinding("queue", "amq.direct", bindings.get(0));
+
+ HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct/queue/queue", "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+
+ bindings = getJsonAsList("/rest/binding/test/amq.direct/queue/queue");
+ assertEquals("Binding should be deleted", 0, bindings.size());
+ }
+
+ public void testDeleteBindingById() throws Exception
+ {
+ Map<String, Object> binding = getJsonAsSingletonList("/rest/binding/test/amq.direct/queue");
+ HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct?id=" + binding.get(Binding.ID), "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue");
+ assertEquals("Binding should be deleted", 0, bindings.size());
+ }
+
+ public void testCreateBinding() throws Exception
+ {
+ String bindingName = getTestName();
+ Map<String, Object> bindingData = new HashMap<String, Object>();
+ bindingData.put(Binding.NAME, bindingName);
+ bindingData.put(Binding.QUEUE, "queue");
+ bindingData.put(Binding.EXCHANGE, "amq.direct");
+
+ HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT");
+ connection.connect();
+ writeJsonRequest(connection, bindingData);
+ int responseCode = connection.getResponseCode();
+ connection.disconnect();
+ assertEquals("Unexpected response code", 201, responseCode);
+ Map<String, Object> binding = getJsonAsSingletonList("/rest/binding/test/amq.direct/queue/" + bindingName);
+
+ Asserts.assertBinding(bindingName, "queue", "amq.direct", binding);
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java
new file mode 100644
index 0000000000..4bbe9155cd
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.server.model.Broker;
+
+public class BrokerRestHttpsTest extends QpidRestTestCase
+{
+ private static final String TRUSTSTORE = "test-profiles/test_resources/ssl/java_client_truststore.jks";
+ private static final String TRUSTSTORE_PASSWORD = "password";
+
+ @Override
+ public void setUp() throws Exception
+ {
+ setSystemProperty("javax.net.debug", "ssl");
+ super.setUp();
+ setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE);
+ setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD);
+ }
+
+ @Override
+ protected void customizeConfiguration() throws ConfigurationException, IOException
+ {
+ setConfigurationProperty("management.enabled", "true");
+ setConfigurationProperty("management.http.enabled", "false");
+ setConfigurationProperty("management.https.enabled", "true");
+ setConfigurationProperty("management.https.port", Integer.toString(getHttpPort()));
+ }
+
+ @Override
+ protected String getHostName()
+ {
+ return "localhost";
+ }
+
+ @Override
+ protected String getProtocol()
+ {
+ return "https";
+ }
+
+ @Override
+ protected HttpURLConnection openManagementConection(String path) throws IOException
+ {
+ URL url = getManagementURL(path);
+ HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
+ ((HttpsURLConnection) httpCon).setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
+ httpCon.setDoOutput(true);
+ return httpCon;
+ }
+
+ public void testGetWithHttps() throws Exception
+ {
+ Map<String, Object> brokerDetails = getJsonAsSingletonList("/rest/broker");
+
+ Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES, Broker.BYTES_RETAINED,
+ Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES, Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED);
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java
new file mode 100644
index 0000000000..f2970e2ba9
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java
@@ -0,0 +1,118 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.common.QpidProperties;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+
+public class BrokerRestTest extends QpidRestTestCase
+{
+
+ private static final String BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE = "authenticationproviders";
+ private static final String BROKER_PORTS_ATTRIBUTE = "ports";
+ private static final String BROKER_VIRTUALHOSTS_ATTRIBUTE = "virtualhosts";
+ private static final String BROKER_STATISTICS_ATTRIBUTE = "statistics";
+
+ public void testGet() throws Exception
+ {
+ Map<String, Object> brokerDetails = getJsonAsSingletonList("/rest/broker");
+
+ assertBrokerAttributes(brokerDetails);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) brokerDetails.get(BROKER_STATISTICS_ATTRIBUTE);
+ Asserts.assertAttributesPresent(statistics, new String[]{ "bytesIn", "messagesOut", "bytesOut", "messagesIn" });
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE);
+ assertEquals("Unexpected number of virtual hosts", 3, virtualhosts.size());
+
+ Asserts.assertVirtualHost("development", find(VirtualHost.NAME, "development", virtualhosts));
+ Asserts.assertVirtualHost("localhost", find(VirtualHost.NAME, "localhost", virtualhosts));
+ Asserts.assertVirtualHost("test", find(VirtualHost.NAME, "test", virtualhosts));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> ports = (List<Map<String, Object>>) brokerDetails.get(BROKER_PORTS_ATTRIBUTE);
+ assertEquals("Unexpected number of ports", 2, ports.size());
+
+ for (Map<String, Object> port : ports)
+ {
+ Asserts.assertPortAttributes(port);
+ }
+
+ String bindingAddress = (String)ports.get(0).get(Port.BINDING_ADDRESS);
+
+ Map<String, Object> amqpPort = find(Port.NAME, bindingAddress + ":" + getPort(), ports);
+ Map<String, Object> httpPort = find(Port.NAME, bindingAddress + ":" + getHttpPort(), ports);
+
+ assertNotNull("Cannot find AMQP port", amqpPort);
+ assertNotNull("Cannot find HTTP port", httpPort);
+
+ @SuppressWarnings("unchecked")
+ Collection<String> port1Protocols = (Collection<String>) amqpPort.get(Port.PROTOCOLS);
+ assertFalse("AMQP protocol list cannot contain HTTP", port1Protocols.contains("HTTP"));
+
+ @SuppressWarnings("unchecked")
+ Collection<String> port2Protocols = (Collection<String>) httpPort.get(Port.PROTOCOLS);
+ assertEquals("Unexpected value of attribute " + Port.PROTOCOLS, new HashSet<String>(Arrays.asList("HTTP")),
+ new HashSet<String>(port2Protocols));
+ }
+
+ protected void assertBrokerAttributes(Map<String, Object> brokerDetails)
+ {
+ Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES,
+ Broker.BYTES_RETAINED, Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES,
+ Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED);
+
+ assertEquals("Unexpected value of attribute " + Broker.BUILD_VERSION, QpidProperties.getBuildVersion(),
+ brokerDetails.get(Broker.BUILD_VERSION));
+ assertEquals("Unexpected value of attribute " + Broker.OPERATING_SYSTEM, System.getProperty("os.name") + " "
+ + System.getProperty("os.version") + " " + System.getProperty("os.arch"),
+ brokerDetails.get(Broker.OPERATING_SYSTEM));
+ assertEquals(
+ "Unexpected value of attribute " + Broker.PLATFORM,
+ System.getProperty("java.vendor") + " "
+ + System.getProperty("java.runtime.version", System.getProperty("java.version")),
+ brokerDetails.get(Broker.PLATFORM));
+ assertEquals("Unexpected value of attribute " + Broker.DURABLE, Boolean.TRUE, brokerDetails.get(Broker.DURABLE));
+ assertEquals("Unexpected value of attribute " + Broker.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(),
+ brokerDetails.get(Broker.LIFETIME_POLICY));
+ assertEquals("Unexpected value of attribute " + Broker.NAME, "Broker", brokerDetails.get(Broker.NAME));
+ assertEquals("Unexpected value of attribute " + Broker.STATE, State.ACTIVE.name(), brokerDetails.get(Broker.STATE));
+
+ assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID));
+ assertNotNull("Unexpected value of attribute statistics", brokerDetails.get(BROKER_STATISTICS_ATTRIBUTE));
+ assertNotNull("Unexpected value of attribute virtualhosts", brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE));
+ assertNotNull("Unexpected value of attribute ports", brokerDetails.get(BROKER_PORTS_ATTRIBUTE));
+ assertNotNull("Unexpected value of attribute authenticationproviders", brokerDetails.get(BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE));
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java
new file mode 100644
index 0000000000..3661b94a7c
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java
@@ -0,0 +1,213 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Session;
+
+public class ConnectionRestTest extends QpidRestTestCase
+{
+ /**
+ * Message number to publish into queue
+ */
+ private static final int MESSAGE_NUMBER = 5;
+ private static final int MESSAGE_SIZE = 6;
+
+ private static final String SESSIONS_ATTRIBUTE = "sessions";
+
+ private javax.jms.Connection _connection;
+ private javax.jms.Session _session;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _connection = getConnection();
+ _session = _connection.createSession(true, javax.jms.Session.SESSION_TRANSACTED);
+ String queueName = getTestQueueName();
+ Destination queue = _session.createQueue(queueName);
+ MessageConsumer consumer = _session.createConsumer(queue);
+ MessageProducer producer = _session.createProducer(queue);
+ _connection.start();
+
+ // send messages
+ for (int i = 0; i < MESSAGE_NUMBER; i++)
+ {
+ producer.send(_session.createTextMessage("Test-" + i));
+ }
+ _session.commit();
+
+ Message m = consumer.receive(1000l);
+ assertNotNull("Message was not received", m);
+ _session.commit();
+
+ // receive the rest of messages for rollback
+ for (int i = 0; i < MESSAGE_NUMBER - 1; i++)
+ {
+ m = consumer.receive(1000l);
+ assertNotNull("Message was not received", m);
+ }
+ _session.rollback();
+
+ // receive them again
+ for (int i = 0; i < MESSAGE_NUMBER - 1; i++)
+ {
+ m = consumer.receive(1000l);
+ assertNotNull("Message was not received", m);
+ }
+ }
+
+ public void testGetAllConnections() throws Exception
+ {
+ List<Map<String, Object>> connections = getJsonAsList("/rest/connection");
+ assertEquals("Unexpected number of connections", 1, connections.size());
+ Asserts.assertConnection(connections.get(0), (AMQConnection) _connection);
+ }
+
+ public void testGetVirtualHostConnections() throws Exception
+ {
+ List<Map<String, Object>> connections = getJsonAsList("/rest/connection/test");
+ assertEquals("Unexpected number of connections", 1, connections.size());
+ Asserts.assertConnection(connections.get(0), (AMQConnection) _connection);
+ }
+
+ public void testGetConnectionByName() throws Exception
+ {
+ // get connection name
+ String connectionName = getConnectionName();
+
+ Map<String, Object> connectionDetails = getJsonAsSingletonList("/rest/connection/test/"
+ + URLDecoder.decode(connectionName, "UTF-8"));
+ assertConnection(connectionDetails);
+ }
+
+ public void testGetAllSessions() throws Exception
+ {
+ List<Map<String, Object>> sessions = getJsonAsList("/rest/session");
+ assertEquals("Unexpected number of sessions", 1, sessions.size());
+ assertSession(sessions.get(0), (AMQSession<?, ?>) _session);
+ }
+
+ public void testGetVirtualHostSessions() throws Exception
+ {
+ List<Map<String, Object>> sessions = getJsonAsList("/rest/session/test");
+ assertEquals("Unexpected number of sessions", 1, sessions.size());
+ assertSession(sessions.get(0), (AMQSession<?, ?>) _session);
+ }
+
+ public void testGetConnectionSessions() throws Exception
+ {
+ // get connection name
+ String connectionName = getConnectionName();
+
+ List<Map<String, Object>> sessions = getJsonAsList("/rest/session/test/"
+ + URLDecoder.decode(connectionName, "UTF-8"));
+ assertEquals("Unexpected number of sessions", 1, sessions.size());
+ assertSession(sessions.get(0), (AMQSession<?, ?>) _session);
+ }
+
+ public void testGetSessionByName() throws Exception
+ {
+ // get connection name
+ String connectionName = getConnectionName();
+
+ List<Map<String, Object>> sessions = getJsonAsList("/rest/session/test/"
+ + URLDecoder.decode(connectionName, "UTF-8") + "/" + ((AMQSession<?, ?>) _session).getChannelId());
+ assertEquals("Unexpected number of sessions", 1, sessions.size());
+ assertSession(sessions.get(0), (AMQSession<?, ?>) _session);
+ }
+
+ private void assertConnection(Map<String, Object> connectionDetails) throws JMSException
+ {
+ Asserts.assertConnection(connectionDetails, (AMQConnection) _connection);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) connectionDetails.get(Asserts.STATISTICS_ATTRIBUTE);
+ assertEquals("Unexpected value of connection statistics attribute " + Connection.BYTES_IN, MESSAGE_NUMBER
+ * MESSAGE_SIZE, statistics.get(Connection.BYTES_IN));
+ assertEquals("Unexpected value of connection statistics attribute " + Connection.BYTES_OUT, MESSAGE_SIZE
+ + ((MESSAGE_NUMBER - 1) * MESSAGE_SIZE) * 2, statistics.get(Connection.BYTES_OUT));
+ assertEquals("Unexpected value of connection statistics attribute " + Connection.MESSAGES_IN, MESSAGE_NUMBER,
+ statistics.get(Connection.MESSAGES_IN));
+ assertEquals("Unexpected value of connection statistics attribute " + Connection.MESSAGES_OUT,
+ MESSAGE_NUMBER * 2 - 1, statistics.get(Connection.MESSAGES_OUT));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> sessions = (List<Map<String, Object>>) connectionDetails.get(SESSIONS_ATTRIBUTE);
+ assertNotNull("Sessions cannot be found", sessions);
+ assertEquals("Unexpected number of sessions", 1, sessions.size());
+ assertSession(sessions.get(0), (AMQSession<?, ?>) _session);
+ }
+
+ private void assertSession(Map<String, Object> sessionData, AMQSession<?, ?> session)
+ {
+ assertNotNull("Session map cannot be null", sessionData);
+ Asserts.assertAttributesPresent(sessionData, Session.AVAILABLE_ATTRIBUTES, Session.STATE, Session.DURABLE,
+ Session.LIFETIME_POLICY, Session.TIME_TO_LIVE, Session.CREATED, Session.UPDATED);
+ assertEquals("Unexpecte value of attribute " + Session.NAME, session.getChannelId() + "",
+ sessionData.get(Session.NAME));
+ assertEquals("Unexpecte value of attribute " + Session.PRODUCER_FLOW_BLOCKED, Boolean.FALSE,
+ sessionData.get(Session.PRODUCER_FLOW_BLOCKED));
+ assertEquals("Unexpecte value of attribute " + Session.CHANNEL_ID, session.getChannelId(),
+ sessionData.get(Session.CHANNEL_ID));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) sessionData.get(Asserts.STATISTICS_ATTRIBUTE);
+ Asserts.assertAttributesPresent(statistics, Session.AVAILABLE_STATISTICS, Session.BYTES_IN, Session.BYTES_OUT,
+ Session.STATE_CHANGED, Session.UNACKNOWLEDGED_BYTES, Session.LOCAL_TRANSACTION_OPEN,
+ Session.XA_TRANSACTION_BRANCH_ENDS, Session.XA_TRANSACTION_BRANCH_STARTS,
+ Session.XA_TRANSACTION_BRANCH_SUSPENDS);
+
+ assertEquals("Unexpecte value of statistic attribute " + Session.UNACKNOWLEDGED_MESSAGES, MESSAGE_NUMBER - 1,
+ statistics.get(Session.UNACKNOWLEDGED_MESSAGES));
+ assertEquals("Unexpecte value of statistic attribute " + Session.LOCAL_TRANSACTION_BEGINS, 4,
+ statistics.get(Session.LOCAL_TRANSACTION_BEGINS));
+ assertEquals("Unexpecte value of statistic attribute " + Session.LOCAL_TRANSACTION_ROLLBACKS, 1,
+ statistics.get(Session.LOCAL_TRANSACTION_ROLLBACKS));
+ assertEquals("Unexpecte value of statistic attribute " + Session.CONSUMER_COUNT, 1,
+ statistics.get(Session.CONSUMER_COUNT));
+ }
+
+ private String getConnectionName() throws IOException
+ {
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> connections = (List<Map<String, Object>>) hostDetails
+ .get(VirtualHostRestTest.VIRTUALHOST_CONNECTIONS_ATTRIBUTE);
+ assertEquals("Unexpected number of connections", 1, connections.size());
+ Map<String, Object> connection = connections.get(0);
+ String connectionName = (String) connection.get(Connection.NAME);
+ return connectionName;
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java
new file mode 100644
index 0000000000..59936427f9
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java
@@ -0,0 +1,67 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.Exchange;
+
+public class ExchangeRestTest extends QpidRestTestCase
+{
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> exchanges = getJsonAsList("/rest/exchange");
+ assertNotNull("Exchanges cannot be null", exchanges);
+ assertTrue("Unexpected number of exchanges", exchanges.size() >= EXPECTED_HOSTS.length * EXPECTED_EXCHANGES.length);
+ for (Map<String, Object> exchange : exchanges)
+ {
+ Asserts.assertExchange((String) exchange.get(Exchange.NAME), (String) exchange.get(Exchange.TYPE), exchange);
+ }
+ }
+
+ public void testGetHostExchanges() throws Exception
+ {
+ List<Map<String, Object>> exchanges = getJsonAsList("/rest/exchange/test");
+ assertNotNull("Users cannot be null", exchanges);
+ assertEquals("Unexpected number of exchanges", 6, EXPECTED_EXCHANGES.length);
+ for (String exchangeName : EXPECTED_EXCHANGES)
+ {
+ Map<String, Object> exchange = find(Exchange.NAME, exchangeName, exchanges);
+ assertExchange(exchangeName, exchange);
+ }
+ }
+
+ public void testGetHostExchangeByName() throws Exception
+ {
+ for (String exchangeName : EXPECTED_EXCHANGES)
+ {
+ Map<String, Object> exchange = getJsonAsSingletonList("/rest/exchange/test/"
+ + URLDecoder.decode(exchangeName, "UTF-8"));
+ assertExchange(exchangeName, exchange);
+ }
+ }
+
+ private void assertExchange(String exchangeName, Map<String, Object> exchange)
+ {
+ assertNotNull("Exchange with name " + exchangeName + " is not found", exchange);
+ String type = (String) exchange.get(Exchange.TYPE);
+ Asserts.assertExchange(exchangeName, type, exchange);
+ if ("direct".equals(type))
+ {
+ assertBindings(exchange);
+ }
+ }
+
+ private void assertBindings(Map<String, Object> exchange)
+ {
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) exchange.get("bindings");
+ for (String queueName : EXPECTED_QUEUES)
+ {
+ Map<String, Object> binding = find(Binding.NAME, queueName, bindings);
+ Asserts.assertBinding(queueName, (String) exchange.get(Exchange.NAME), binding);
+ }
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java
new file mode 100644
index 0000000000..c64fd6e1da
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.util.List;
+import java.util.Map;
+
+public class LogRecordsRestTest extends QpidRestTestCase
+{
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> logs = getJsonAsList("/rest/logrecords");
+ assertNotNull("Logs data cannot be null", logs);
+ assertTrue("Logs are not found", logs.size() > 0);
+ Map<String, Object> record = find("message", "[Broker] BRK-1004 : Qpid Broker Ready", logs);
+
+ assertNotNull("BRK-1004 message is not found", record);
+ assertNotNull("Message id cannot be null", record.get("id"));
+ assertNotNull("Message timestamp cannot be null", record.get("timestamp"));
+ assertEquals("Unexpected log level", "INFO", record.get("level"));
+ assertEquals("Unexpected thread", "main", record.get("thread"));
+ assertEquals("Unexpected logger", "qpid.message.broker.ready", record.get("logger"));
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java
new file mode 100644
index 0000000000..492df43957
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java
@@ -0,0 +1,354 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.jms.BytesMessage;
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+
+public class MessagesRestTest extends QpidRestTestCase
+{
+
+ /**
+ * Message number to publish into queue
+ */
+ private static final int MESSAGE_NUMBER = 12;
+
+ private Connection _connection;
+ private Session _session;
+ private MessageProducer _producer;
+ private long _startTime;
+ private long _ttl;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _startTime = System.currentTimeMillis();
+ _connection = getConnection();
+ _session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ String queueName = getTestQueueName();
+ Destination queue = _session.createQueue(queueName);
+ _session.createConsumer(queue);
+ _producer = _session.createProducer(queue);
+
+ _ttl = TimeUnit.DAYS.toMillis(1);
+ for (int i = 0; i < MESSAGE_NUMBER; i++)
+ {
+ Message m = _session.createTextMessage("Test-" + i);
+ m.setIntProperty("index", i);
+ if (i % 2 == 0)
+ {
+ _producer.send(m);
+ }
+ else
+ {
+ _producer.send(m, DeliveryMode.NON_PERSISTENT, 5, _ttl);
+ }
+ }
+ _session.commit();
+ }
+
+ public void testGet() throws Exception
+ {
+ String queueName = getTestQueueName();
+ List<Map<String, Object>> messages = getJsonAsList("/rest/message/test/" + queueName);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size());
+ int position = 0;
+ for (Map<String, Object> message : messages)
+ {
+ assertMessage(position, message);
+ position++;
+ }
+ }
+
+ public void testGetMessageContent() throws Exception
+ {
+ String queueName = getTestQueueName();
+
+ // add bytes message
+ BytesMessage byteMessage = _session.createBytesMessage();
+ byte[] messageBytes = "Test".getBytes();
+ byteMessage.writeBytes(messageBytes);
+ byteMessage.setStringProperty("test", "value");
+ _producer.send(byteMessage);
+ _session.commit();
+
+ // get message IDs
+ List<Long> ids = getMesssageIds(queueName);
+
+ Map<String, Object> message = getJsonAsMap("/rest/message/test/" + queueName + "/" + ids.get(0));
+ assertMessageAttributes(message);
+ assertMessageAttributeValues(message, true);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> headers = (Map<String, Object>) message.get("headers");
+ assertNotNull("Message headers are not found", headers);
+ assertEquals("Unexpected message header", 0, headers.get("index"));
+
+ Long lastMessageId = ids.get(ids.size() - 1);
+ message = getJsonAsMap("/rest/message/test/" + queueName + "/" + lastMessageId);
+ assertMessageAttributes(message);
+ assertEquals("Unexpected message attribute mimeType", "application/octet-stream", message.get("mimeType"));
+ assertEquals("Unexpected message attribute size", 4, message.get("size"));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> bytesMessageHeader = (Map<String, Object>) message.get("headers");
+ assertNotNull("Message headers are not found", bytesMessageHeader);
+ assertEquals("Unexpected message header", "value", bytesMessageHeader.get("test"));
+
+ // get content
+ HttpURLConnection connection = openManagementConection("/rest/message-content/test/" + queueName + "/"
+ + lastMessageId, "GET");
+ connection.connect();
+ byte[] data = readConnectionInputStream(connection);
+ assertTrue("Unexpected message", Arrays.equals(messageBytes, data));
+
+ }
+
+ public void testPostMoveMessages() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String queueName2 = queueName + "_2";
+ Destination queue2 = _session.createQueue(queueName2);
+ _session.createConsumer(queue2);
+
+ // get message IDs
+ List<Long> ids = getMesssageIds(queueName);
+
+ // move half of the messages
+ int movedNumber = ids.size() / 2;
+ List<Long> movedMessageIds = new ArrayList<Long>();
+ for (int i = 0; i < movedNumber; i++)
+ {
+ movedMessageIds.add(ids.remove(i));
+ }
+
+ // move messages
+ HttpURLConnection connection = openManagementConection("/rest/message/test/" + queueName, "POST");
+
+ Map<String, Object> messagesData = new HashMap<String, Object>();
+ messagesData.put("messages", movedMessageIds);
+ messagesData.put("destinationQueue", queueName2);
+ messagesData.put("move", Boolean.TRUE);
+
+ writeJsonRequest(connection, messagesData);
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+
+ // check messages on target queue
+ List<Map<String, Object>> messages = getJsonAsList("/rest/message/test/" + queueName2);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", movedMessageIds.size(), messages.size());
+ for (Long id : movedMessageIds)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+
+ // check messages on original queue
+ messages = getJsonAsList("/rest/message/test/" + queueName);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", ids.size(), messages.size());
+ for (Long id : ids)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+ for (Long id : movedMessageIds)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertNull("Moved message " + id + " is found on original queue", message);
+ }
+ }
+
+ public void testPostCopyMessages() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String queueName2 = queueName + "_2";
+ Destination queue2 = _session.createQueue(queueName2);
+ _session.createConsumer(queue2);
+
+ // get message IDs
+ List<Long> ids = getMesssageIds(queueName);
+
+ // copy half of the messages
+ int copyNumber = ids.size() / 2;
+ List<Long> copyMessageIds = new ArrayList<Long>();
+ for (int i = 0; i < copyNumber; i++)
+ {
+ copyMessageIds.add(ids.remove(i));
+ }
+
+ // copy messages
+ HttpURLConnection connection = openManagementConection("/rest/message/test/" + queueName, "POST");
+
+ Map<String, Object> messagesData = new HashMap<String, Object>();
+ messagesData.put("messages", copyMessageIds);
+ messagesData.put("destinationQueue", queueName2);
+
+ writeJsonRequest(connection, messagesData);
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+
+ // check messages on target queue
+ List<Map<String, Object>> messages = getJsonAsList("/rest/message/test/" + queueName2);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", copyMessageIds.size(), messages.size());
+ for (Long id : copyMessageIds)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+
+ // check messages on original queue
+ messages = getJsonAsList("/rest/message/test/" + queueName);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size());
+ for (Long id : ids)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+ for (Long id : copyMessageIds)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+ }
+
+ public void testDeleteMessages() throws Exception
+ {
+ String queueName = getTestQueueName();
+
+ // get message IDs
+ List<Long> ids = getMesssageIds(queueName);
+
+ // delete half of the messages
+ int deleteNumber = ids.size() / 2;
+ StringBuilder queryString = new StringBuilder();
+ List<Long> deleteMessageIds = new ArrayList<Long>();
+ for (int i = 0; i < deleteNumber; i++)
+ {
+ Long id = ids.remove(i);
+ deleteMessageIds.add(id);
+ if (queryString.length() > 0)
+ {
+ queryString.append("&");
+ }
+ queryString.append("id=").append(id);
+ }
+
+ // delete messages
+ HttpURLConnection connection = openManagementConection(
+ "/rest/message/test/" + queueName + "?" + queryString.toString(), "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+
+ // check messages on queue
+ List<Map<String, Object>> messages = getJsonAsList("/rest/message/test/" + queueName);
+ assertNotNull("Messages are not found", messages);
+ assertEquals("Unexpected number of messages", ids.size(), messages.size());
+ for (Long id : ids)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertMessageAttributes(message);
+ }
+ for (Long id : deleteMessageIds)
+ {
+ Map<String, Object> message = find("id", id.intValue(), messages);
+ assertNull("Message with id " + id + " was not deleted", message);
+ }
+ }
+
+ private List<Long> getMesssageIds(String queueName) throws IOException, JsonParseException, JsonMappingException
+ {
+ List<Map<String, Object>> messages = getJsonAsList("/rest/message/test/" + queueName);
+ List<Long> ids = new ArrayList<Long>();
+ for (Map<String, Object> message : messages)
+ {
+ ids.add(((Number) message.get("id")).longValue());
+ }
+ return ids;
+ }
+
+ private void assertMessage(int position, Map<String, Object> message)
+ {
+ assertMessageAttributes(message);
+
+ assertEquals("Unexpected message attribute position", position, message.get("position"));
+ assertEquals("Unexpected message attribute size", position < 10 ? 6 : 7, message.get("size"));
+ boolean even = position % 2 == 0;
+ assertMessageAttributeValues(message, even);
+ }
+
+ private void assertMessageAttributeValues(Map<String, Object> message, boolean even)
+ {
+ if (even)
+ {
+ assertEquals("Unexpected message attribute expirationTime", 0, message.get("expirationTime"));
+ assertEquals("Unexpected message attribute priority", 4, message.get("priority"));
+ assertEquals("Unexpected message attribute persistent", Boolean.TRUE, message.get("persistent"));
+ }
+ else
+ {
+ assertEquals("Unexpected message attribute expirationTime", ((Number) message.get("timestamp")).longValue()
+ + _ttl, message.get("expirationTime"));
+ assertEquals("Unexpected message attribute priority", 5, message.get("priority"));
+ assertEquals("Unexpected message attribute persistent", Boolean.FALSE, message.get("persistent"));
+ }
+ assertEquals("Unexpected message attribute mimeType", "text/plain", message.get("mimeType"));
+ assertEquals("Unexpected message attribute userId", "guest", message.get("userId"));
+ assertEquals("Unexpected message attribute deliveryCount", 0, message.get("deliveryCount"));
+ assertEquals("Unexpected message attribute state", "Available", message.get("state"));
+ }
+
+ private void assertMessageAttributes(Map<String, Object> message)
+ {
+ assertNotNull("Message map cannot be null", message);
+ assertNotNull("Unexpected message attribute deliveryCount", message.get("deliveryCount"));
+ assertNotNull("Unexpected message attribute state", message.get("state"));
+ assertNotNull("Unexpected message attribute id", message.get("id"));
+ assertNotNull("Message arrivalTime cannot be null", message.get("arrivalTime"));
+ assertNotNull("Message timestamp cannot be null", message.get("timestamp"));
+ assertTrue("Message arrivalTime cannot be null", ((Number) message.get("arrivalTime")).longValue() > _startTime);
+ assertNotNull("Message messageId cannot be null", message.get("messageId"));
+ assertNotNull("Unexpected message attribute mimeType", message.get("mimeType"));
+ assertNotNull("Unexpected message attribute userId", message.get("userId"));
+ assertNotNull("Message priority cannot be null", message.get("priority"));
+ assertNotNull("Message expirationTime cannot be null", message.get("expirationTime"));
+ assertNotNull("Message persistent cannot be null", message.get("persistent"));
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java
new file mode 100644
index 0000000000..49f163baae
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java
@@ -0,0 +1,41 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.Port;
+
+public class PortRestTest extends QpidRestTestCase
+{
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> ports = getJsonAsList("/rest/port/");
+ assertNotNull("Port data cannot be null", ports);
+ assertEquals("Unexpected number of ports", 2, ports.size());
+ int[] expectedPorts = { getPort(), getHttpPort() };
+ for (int port : expectedPorts)
+ {
+ String portName = "0.0.0.0:" + port;
+ Map<String, Object> portData = find(Port.NAME, portName, ports);
+ assertNotNull("Port " + portName + " is not found", portData);
+ Asserts.assertPortAttributes(portData);
+ }
+ }
+
+ public void testGetPort() throws Exception
+ {
+ List<Map<String, Object>> ports = getJsonAsList("/rest/port/");
+ assertNotNull("Ports data cannot be null", ports);
+ assertEquals("Unexpected number of ports", 2, ports.size());
+ for (Map<String, Object> portMap : ports)
+ {
+ String portName = (String) portMap.get(Port.NAME);
+ assertNotNull("Port name attribute is not found", portName);
+ Map<String, Object> portData = getJsonAsSingletonList("/rest/port/" + URLDecoder.decode(portName, "UTF-8"));
+ assertNotNull("Port " + portName + " is not found", portData);
+ Asserts.assertPortAttributes(portData);
+ }
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java
new file mode 100644
index 0000000000..e83341de80
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java
@@ -0,0 +1,245 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.Logger;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+public class QpidRestTestCase extends QpidBrokerTestCase
+{
+ private static final Logger LOGGER = Logger.getLogger(QpidRestTestCase.class);
+
+ public static final String[] EXPECTED_HOSTS = { "development", "test", "localhost" };
+ public static final String[] EXPECTED_QUEUES = { "queue", "ping" };
+ public static final String[] EXPECTED_EXCHANGES = { "amq.fanout", "amq.match", "amq.direct", "amq.topic",
+ "qpid.management", "<<default>>" };
+
+ private int _httpPort;
+ private String _hostName;
+ private List<HttpURLConnection> _httpConnections;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ _httpConnections = new ArrayList<HttpURLConnection>();
+ _hostName = InetAddress.getLocalHost().getHostName();
+ _httpPort = findFreePort();
+ customizeConfiguration();
+ super.setUp();
+
+ }
+
+ protected void customizeConfiguration() throws ConfigurationException, IOException
+ {
+ setConfigurationProperty("management.enabled", "false");
+ setConfigurationProperty("management.http.enabled", "true");
+ setConfigurationProperty("management.https.enabled", "false");
+ setConfigurationProperty("management.http.port", Integer.toString(_httpPort));
+ }
+
+ public void teearDown() throws Exception
+ {
+ for (HttpURLConnection connection : _httpConnections)
+ {
+ try
+ {
+ connection.disconnect();
+ }
+ catch (Exception e)
+ {
+ // ignore
+ }
+ }
+ super.tearDown();
+ }
+
+ protected int getHttpPort()
+ {
+ return _httpPort;
+ }
+
+ protected String getHostName()
+ {
+ return _hostName;
+ }
+
+ protected String getProtocol()
+ {
+ return "http";
+ }
+
+ protected String getManagementURL()
+ {
+ return getProtocol() + "://" + getHostName() + ":" + getHttpPort();
+ }
+
+ protected URL getManagementURL(String path) throws MalformedURLException
+ {
+ return new URL(getManagementURL() + path);
+ }
+
+ protected HttpURLConnection openManagementConection(String path) throws IOException
+ {
+ URL url = getManagementURL(path);
+ HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
+ httpCon.setDoOutput(true);
+ return httpCon;
+ }
+
+ protected HttpURLConnection openManagementConection(String path, String method) throws IOException
+ {
+ HttpURLConnection httpCon = openManagementConection(path);
+ httpCon.setRequestMethod(method);
+ return httpCon;
+ }
+
+ protected List<Map<String, Object>> readJsonResponseAsList(HttpURLConnection connection) throws IOException,
+ JsonParseException, JsonMappingException
+ {
+ byte[] data = readConnectionInputStream(connection);
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ TypeReference<List<LinkedHashMap<String, Object>>> typeReference = new TypeReference<List<LinkedHashMap<String, Object>>>()
+ {
+ };
+ List<Map<String, Object>> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference);
+ return providedObject;
+ }
+
+ protected Map<String, Object> readJsonResponseAsMap(HttpURLConnection connection) throws IOException,
+ JsonParseException, JsonMappingException
+ {
+ byte[] data = readConnectionInputStream(connection);
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ TypeReference<LinkedHashMap<String, Object>> typeReference = new TypeReference<LinkedHashMap<String, Object>>()
+ {
+ };
+ Map<String, Object> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference);
+ return providedObject;
+ }
+
+ protected byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException
+ {
+ InputStream is = connection.getInputStream();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int len = -1;
+ while ((len = is.read(buffer)) != -1)
+ {
+ baos.write(buffer, 0, len);
+ }
+ if (LOGGER.isTraceEnabled())
+ {
+ LOGGER.trace("RESPONSE:" + new String(baos.toByteArray()));
+ }
+ return baos.toByteArray();
+ }
+
+ protected void writeJsonRequest(HttpURLConnection connection, Map<String, Object> data) throws JsonGenerationException,
+ JsonMappingException, IOException
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(connection.getOutputStream(), data);
+ }
+
+ protected Map<String, Object> find(String name, Object value, List<Map<String, Object>> data)
+ {
+ for (Map<String, Object> map : data)
+ {
+ Object mapValue = map.get(name);
+ if (value.equals(mapValue))
+ {
+ return map;
+ }
+ }
+ return null;
+ }
+
+ protected Map<String, Object> find(Map<String, Object> searchAttributes, List<Map<String, Object>> data)
+ {
+ for (Map<String, Object> map : data)
+ {
+ boolean equals = true;
+ for (Map.Entry<String, Object> entry : searchAttributes.entrySet())
+ {
+ Object mapValue = map.get(entry.getKey());
+ if (!entry.getValue().equals(mapValue))
+ {
+ equals = false;
+ break;
+ }
+ }
+ if (equals)
+ {
+ return map;
+ }
+ }
+ return null;
+ }
+
+ protected Map<String, Object> getJsonAsSingletonList(String path) throws IOException
+ {
+ List<Map<String, Object>> response = getJsonAsList(path);
+
+ assertNotNull("Response cannot be null", response);
+ assertEquals("Unexpected response", 1, response.size());
+ return response.get(0);
+ }
+
+ protected List<Map<String, Object>> getJsonAsList(String path) throws IOException, JsonParseException,
+ JsonMappingException
+ {
+ HttpURLConnection connection = openManagementConection(path, "GET");
+ connection.connect();
+ List<Map<String, Object>> response = readJsonResponseAsList(connection);
+ return response;
+ }
+
+ protected Map<String, Object> getJsonAsMap(String path) throws IOException
+ {
+ HttpURLConnection connection = openManagementConection(path, "GET");
+ connection.connect();
+ Map<String, Object> response = readJsonResponseAsMap(connection);
+ return response;
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java
new file mode 100644
index 0000000000..5f11b3fb1d
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java
@@ -0,0 +1,225 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.Consumer;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.Queue;
+
+public class QueueRestTest extends QpidRestTestCase
+{
+ private static final String QUEUE_ATTRIBUTE_CONSUMERS = "consumers";
+ private static final String QUEUE_ATTRIBUTE_BINDINGS = "bindings";
+
+ /**
+ * Message number to publish into queue
+ */
+ private static final int MESSAGE_NUMBER = 2;
+ private static final int MESSAGE_PAYLOAD_SIZE = 6;
+ private static final int ENQUEUED_MESSAGES = 1;
+ private static final int DEQUEUED_MESSAGES = 1;
+ private static final int ENQUEUED_BYTES = MESSAGE_PAYLOAD_SIZE;
+ private static final int DEQUEUED_BYTES = MESSAGE_PAYLOAD_SIZE;
+
+ private Connection _connection;
+
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _connection = getConnection();
+ Session session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ String queueName = getTestQueueName();
+ Destination queue = session.createQueue(queueName);
+ MessageConsumer consumer = session.createConsumer(queue);
+ MessageProducer producer = session.createProducer(queue);
+
+ for (int i = 0; i < MESSAGE_NUMBER; i++)
+ {
+ producer.send(session.createTextMessage("Test-" + i));
+ }
+ session.commit();
+ _connection.start();
+ Message m = consumer.receive(1000l);
+ assertNotNull("Message is not received", m);
+ session.commit();
+ }
+
+ public void testGetVirtualHostQueues() throws Exception
+ {
+ String queueName = getTestQueueName();
+ List<Map<String, Object>> queues = getJsonAsList("/rest/queue/test");
+ assertEquals("Unexpected number of queues", EXPECTED_QUEUES.length + 1, queues.size());
+ String[] expectedQueues = new String[EXPECTED_QUEUES.length + 1];
+ System.arraycopy(EXPECTED_QUEUES, 0, expectedQueues, 0, EXPECTED_QUEUES.length);
+ expectedQueues[EXPECTED_QUEUES.length] = queueName;
+
+ for (String name : expectedQueues)
+ {
+ Map<String, Object> queueDetails = find(Queue.NAME, name, queues);
+ Asserts.assertQueue(name, "standard", queueDetails);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS);
+ assertNotNull("Queue bindings are not found", bindings);
+ assertEquals("Unexpected number of bindings", 2, bindings.size());
+
+ Map<String, Object> defaultExchangeBinding = find(Binding.EXCHANGE, "<<default>>", bindings);
+ Map<String, Object> directExchangeBinding = find(Binding.EXCHANGE, "amq.direct", bindings);
+ Asserts.assertBinding(name, "<<default>>", defaultExchangeBinding);
+ Asserts.assertBinding(name, "amq.direct", directExchangeBinding);
+ }
+ }
+
+ public void testGetByName() throws Exception
+ {
+ String queueName = getTestQueueName();
+ Map<String, Object> queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName);
+ Asserts.assertQueue(queueName, "standard", queueDetails);
+ assertStatistics(queueDetails);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS);
+ assertNotNull("Queue bindings are not found", bindings);
+ assertEquals("Unexpected number of bindings", 2, bindings.size());
+
+ Map<String, Object> defaultExchangeBinding = find(Binding.EXCHANGE, "<<default>>", bindings);
+ Map<String, Object> directExchangeBinding = find(Binding.EXCHANGE, "amq.direct", bindings);
+ Asserts.assertBinding(queueName, "<<default>>", defaultExchangeBinding);
+ Asserts.assertBinding(queueName, "amq.direct", directExchangeBinding);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> consumers = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_CONSUMERS);
+ assertNotNull("Queue consumers are not found", consumers);
+ assertEquals("Unexpected number of consumers", 1, consumers.size());
+ assertConsumer(consumers.get(0));
+ }
+
+ public void testPutCreateBinding() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String bindingName = queueName + 2;
+ String[] exchanges = { "amq.direct", "amq.fanout", "amq.topic", "amq.match", "qpid.management", "<<default>>" };
+
+ for (int i = 0; i < exchanges.length; i++)
+ {
+ createBinding(bindingName, exchanges[i], queueName);
+ }
+
+ Map<String, Object> queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName);
+ Asserts.assertQueue(queueName, "standard", queueDetails);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) queueDetails.get(QUEUE_ATTRIBUTE_BINDINGS);
+ assertNotNull("Queue bindings are not found", bindings);
+ assertEquals("Unexpected number of bindings", exchanges.length + 2, bindings.size());
+
+ Map<String, Object> searchAttributes = new HashMap<String, Object>();
+ searchAttributes.put(Binding.NAME, bindingName);
+
+ for (int i = 0; i < exchanges.length; i++)
+ {
+ searchAttributes.put(Binding.EXCHANGE, exchanges[i]);
+ Map<String, Object> binding = find(searchAttributes, bindings);
+ Asserts.assertBinding(bindingName, queueName, exchanges[i], binding);
+ }
+ }
+
+ private void createBinding(String bindingName, String exchangeName, String queueName) throws IOException
+ {
+ HttpURLConnection connection = openManagementConection(
+ "/rest/binding/test/" + URLDecoder.decode(exchangeName, "UTF-8") + "/" + queueName + "/" + bindingName,
+ "PUT");
+
+ Map<String, Object> bindingData = new HashMap<String, Object>();
+ bindingData.put(Binding.NAME, bindingName);
+ bindingData.put(Binding.EXCHANGE, exchangeName);
+ bindingData.put(Binding.QUEUE, queueName);
+
+ writeJsonRequest(connection, bindingData);
+ assertEquals("Unexpected response code", 201, connection.getResponseCode());
+
+ connection.disconnect();
+ }
+
+ private void assertConsumer(Map<String, Object> consumer)
+ {
+ assertNotNull("Consumer map should not be null", consumer);
+ Asserts.assertAttributesPresent(consumer, Consumer.AVAILABLE_ATTRIBUTES, Consumer.STATE, Consumer.TIME_TO_LIVE,
+ Consumer.CREATED, Consumer.UPDATED, Consumer.SETTLEMENT_MODE, Consumer.EXCLUSIVE, Consumer.SELECTOR,
+ Consumer.NO_LOCAL);
+
+ assertEquals("Unexpected binding attribute " + Consumer.NAME, "1", consumer.get(Consumer.NAME));
+ assertEquals("Unexpected binding attribute " + Consumer.DURABLE, Boolean.FALSE, consumer.get(Consumer.DURABLE));
+ assertEquals("Unexpected binding attribute " + Consumer.LIFETIME_POLICY, LifetimePolicy.AUTO_DELETE.name(),
+ consumer.get(Consumer.LIFETIME_POLICY));
+ assertEquals("Unexpected binding attribute " + Consumer.DISTRIBUTION_MODE, "MOVE",
+ consumer.get(Consumer.DISTRIBUTION_MODE));
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) consumer.get(Asserts.STATISTICS_ATTRIBUTE);
+ assertNotNull("Consumer statistics is not present", statistics);
+ Asserts.assertAttributesPresent(statistics, Consumer.AVAILABLE_STATISTICS, Consumer.STATE_CHANGED);
+ }
+
+ private void assertStatistics(Map<String, Object> queueDetails)
+ {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) queueDetails.get(Asserts.STATISTICS_ATTRIBUTE);
+ assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES,
+ statistics.get(Queue.PERSISTENT_DEQUEUED_MESSAGES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.QUEUE_DEPTH_MESSAGES, ENQUEUED_MESSAGES,
+ statistics.get(Queue.QUEUE_DEPTH_MESSAGES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.CONSUMER_COUNT, 1,
+ statistics.get(Queue.CONSUMER_COUNT));
+ assertEquals("Unexpected queue statistics attribute " + Queue.CONSUMER_COUNT_WITH_CREDIT, 1,
+ statistics.get(Queue.CONSUMER_COUNT_WITH_CREDIT));
+ assertEquals("Unexpected queue statistics attribute " + Queue.BINDING_COUNT, 2, statistics.get(Queue.BINDING_COUNT));
+ assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES,
+ statistics.get(Queue.PERSISTENT_DEQUEUED_MESSAGES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_DEQUEUED_MESSAGES, DEQUEUED_MESSAGES,
+ statistics.get(Queue.TOTAL_DEQUEUED_MESSAGES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_DEQUEUED_BYTES, DEQUEUED_BYTES,
+ statistics.get(Queue.TOTAL_DEQUEUED_BYTES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_DEQUEUED_BYTES, DEQUEUED_BYTES,
+ statistics.get(Queue.TOTAL_DEQUEUED_BYTES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.PERSISTENT_ENQUEUED_BYTES, ENQUEUED_BYTES
+ + DEQUEUED_BYTES, statistics.get(Queue.PERSISTENT_ENQUEUED_BYTES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.TOTAL_ENQUEUED_BYTES, ENQUEUED_BYTES + DEQUEUED_BYTES,
+ statistics.get(Queue.TOTAL_ENQUEUED_BYTES));
+ assertEquals("Unexpected queue statistics attribute " + Queue.QUEUE_DEPTH_BYTES, ENQUEUED_BYTES,
+ statistics.get(Queue.QUEUE_DEPTH_BYTES));
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java
new file mode 100644
index 0000000000..943466eda7
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java
@@ -0,0 +1,22 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.List;
+import java.util.Map;
+
+public class SaslRestTest extends QpidRestTestCase
+{
+ public void testGet() throws Exception
+ {
+ Map<String, Object> saslData = getJsonAsMap("/rest/sasl");
+ assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms"));
+
+ @SuppressWarnings("unchecked")
+ List<String> mechanisms = (List<String>) saslData.get("mechanisms");
+ String[] expectedMechanisms = { "AMQPLAIN", "PLAIN", "CRAM-MD5" };
+ for (String mechanism : expectedMechanisms)
+ {
+ assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism));
+ }
+ }
+
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java
new file mode 100644
index 0000000000..b01e1d44b8
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.List;
+import java.util.Map;
+
+public class StructureRestTest extends QpidRestTestCase
+{
+
+ public void testGet() throws Exception
+ {
+ Map<String, Object> structure = getJsonAsMap("/rest/structure");
+ assertNotNull("Structure data cannot be null", structure);
+ assertNode(structure, "Broker");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) structure.get("virtualhosts");
+ assertEquals("Unexpected number of virtual hosts", 3, virtualhosts.size());
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> ports = (List<Map<String, Object>>) structure.get("ports");
+ assertEquals("Unexpected number of ports", 2, ports.size());
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> providers = (List<Map<String, Object>>) structure.get("authenticationproviders");
+ assertEquals("Unexpected number of authentication providers", 1, providers.size());
+
+ for (String hostName : EXPECTED_HOSTS)
+ {
+ Map<String, Object> host = find("name", hostName, virtualhosts);
+ assertNotNull("Host " + hostName + " is not found ", host);
+ assertNode(host, hostName);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) host.get("queues");
+ assertNotNull("Host " + hostName + " queues are not found ", queues);
+ for (String queueName : EXPECTED_QUEUES)
+ {
+ Map<String, Object> queue = find("name", queueName, queues);
+ assertNotNull(hostName + " queue " + queueName + " is not found ", queue);
+ assertNode(queue, queueName);
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) queue.get("bindings");
+ assertNotNull(hostName + " queue " + queueName + " bindings are not found ", queues);
+ for (Map<String, Object> binding : bindings)
+ {
+ assertNode(binding, queueName);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> exchanges = (List<Map<String, Object>>) host.get("exchanges");
+ assertNotNull("Host " + hostName + " exchanges are not found ", exchanges);
+ for (String exchangeName : EXPECTED_EXCHANGES)
+ {
+ Map<String, Object> exchange = find("name", exchangeName, exchanges);
+ assertNotNull("Exchange " + exchangeName + " is not found ", exchange);
+ assertNode(exchange, exchangeName);
+ if ("amq.direct".equalsIgnoreCase(exchangeName) || "<<default>>".equalsIgnoreCase(exchangeName))
+ {
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> bindings = (List<Map<String, Object>>) exchange.get("bindings");
+ assertNotNull(hostName + " exchange " + exchangeName + " bindings are not found ", bindings);
+ for (String queueName : EXPECTED_QUEUES)
+ {
+ Map<String, Object> binding = find("name", queueName, bindings);
+ assertNotNull(hostName + " exchange " + exchangeName + " binding " + queueName + " is not found", binding);
+ assertNode(binding, queueName);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> aliases = (List<Map<String, Object>>) host.get("virtualhostaliases");
+ assertNotNull("Host " + hostName + " aliaces are not found ", aliases);
+ assertEquals("Unexpected aliaces size", 1, aliases.size());
+ assertNode(aliases.get(0), hostName);
+ }
+
+ int[] expectedPorts = { getPort(), getHttpPort() };
+ for (int port : expectedPorts)
+ {
+ String portName = "0.0.0.0:" + port;
+ Map<String, Object> portData = find("name", portName, ports);
+ assertNotNull("Port " + portName + " is not found ", portData);
+ assertNode(portData, portName);
+ }
+ }
+
+ private void assertNode(Map<String, Object> node, String name)
+ {
+ assertEquals("Unexpected name", name, node.get("name"));
+ assertNotNull("Unexpected id", node.get("id"));
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java
new file mode 100644
index 0000000000..378b349a99
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java
@@ -0,0 +1,92 @@
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.User;
+
+public class UserRestTest extends QpidRestTestCase
+{
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> users = getJsonAsList("/rest/user");
+ assertNotNull("Users cannot be null", users);
+ assertTrue("Unexpected number of users", users.size() > 1);
+ for (Map<String, Object> user : users)
+ {
+ assertUser(user);
+ }
+ }
+
+ public void testGetUserByName() throws Exception
+ {
+ List<Map<String, Object>> users = getJsonAsList("/rest/user");
+ assertNotNull("Users cannot be null", users);
+ assertTrue("Unexpected number of users", users.size() > 1);
+ for (Map<String, Object> user : users)
+ {
+ assertNotNull("Attribute " + User.ID, user.get(User.ID));
+ String userName = (String) user.get(User.NAME);
+ assertNotNull("Attribute " + User.NAME, userName);
+ Map<String, Object> userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/"
+ + userName);
+ assertUser(userDetails);
+ assertEquals("Unexpected user name", userName, userDetails.get(User.NAME));
+ }
+ }
+
+ public void testPut() throws Exception
+ {
+ String userName = getTestName();
+ HttpURLConnection connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager/"
+ + userName, "PUT");
+
+ Map<String, Object> userData = new HashMap<String, Object>();
+ userData.put(User.NAME, userName);
+ userData.put(User.PASSWORD, userName);
+
+ writeJsonRequest(connection, userData);
+ assertEquals("Unexpected response code", 201, connection.getResponseCode());
+
+ connection.disconnect();
+
+ Map<String, Object> userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/"
+ + userName);
+ assertUser(userDetails);
+ assertEquals("Unexpected user name", userName, userDetails.get(User.NAME));
+ }
+
+ public void testDelete() throws Exception
+ {
+ // add user
+ String userName = getTestName();
+ HttpURLConnection connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager/"
+ + userName, "PUT");
+
+ Map<String, Object> userData = new HashMap<String, Object>();
+ userData.put(User.NAME, userName);
+ userData.put(User.PASSWORD, userName);
+
+ writeJsonRequest(connection, userData);
+ assertEquals("Unexpected response code", 201, connection.getResponseCode());
+ connection.disconnect();
+
+ Map<String, Object> userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/"
+ + userName);
+ String id = (String) userDetails.get(User.ID);
+
+ connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager?id=" + id, "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> users = getJsonAsList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName);
+ assertEquals("User should be deleted", 0, users.size());
+ }
+
+ private void assertUser(Map<String, Object> user)
+ {
+ assertNotNull("Attribute " + User.ID, user.get(User.ID));
+ assertNotNull("Attribute " + User.NAME, user.get(User.NAME));
+ }
+}
diff --git a/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java
new file mode 100644
index 0000000000..17f1aaaf7b
--- /dev/null
+++ b/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java
@@ -0,0 +1,434 @@
+/*
+ *
+ * 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.management.plugin.servlet.rest;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jms.Session;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.JsonMappingException;
+
+public class VirtualHostRestTest extends QpidRestTestCase
+{
+ private static final String VIRTUALHOST_EXCHANGES_ATTRIBUTE = "exchanges";
+ public static final String VIRTUALHOST_QUEUES_ATTRIBUTE = "queues";
+ public static final String VIRTUALHOST_CONNECTIONS_ATTRIBUTE = "connections";
+
+ private AMQConnection _connection;
+
+ public void testGet() throws Exception
+ {
+ List<Map<String, Object>> hosts = getJsonAsList("/rest/virtualhost/");
+ assertNotNull("Hosts data cannot be null", hosts);
+ assertEquals("Unexpected number of hosts", 3, hosts.size());
+ for (String hostName : EXPECTED_HOSTS)
+ {
+ Map<String, Object> host = find("name", hostName, hosts);
+ Asserts.assertVirtualHost(hostName, host);
+ }
+ }
+
+ public void testGetHost() throws Exception
+ {
+ // create AMQP connection to get connection JSON details
+ _connection = (AMQConnection) getConnection();
+ _connection.createSession(true, Session.SESSION_TRANSACTED);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+ Asserts.assertVirtualHost("test", hostDetails);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> statistics = (Map<String, Object>) hostDetails.get(Asserts.STATISTICS_ATTRIBUTE);
+ assertEquals("Unexpected number of exchanges in statistics", 6, statistics.get(VirtualHost.EXCHANGE_COUNT));
+ assertEquals("Unexpected number of queues in statistics", 2, statistics.get(VirtualHost.QUEUE_COUNT));
+ assertEquals("Unexpected number of connections in statistics", 1, statistics.get(VirtualHost.CONNECTION_COUNT));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_EXCHANGES_ATTRIBUTE);
+ assertEquals("Unexpected number of exchanges", 6, exchanges.size());
+ Asserts.assertDurableExchange("amq.fanout", "fanout", find(Exchange.NAME, "amq.fanout", exchanges));
+ Asserts.assertDurableExchange("qpid.management", "management", find(Exchange.NAME, "qpid.management", exchanges));
+ Asserts.assertDurableExchange("amq.topic", "topic", find(Exchange.NAME, "amq.topic", exchanges));
+ Asserts.assertDurableExchange("amq.direct", "direct", find(Exchange.NAME, "amq.direct", exchanges));
+ Asserts.assertDurableExchange("amq.match", "headers", find(Exchange.NAME, "amq.match", exchanges));
+ Asserts.assertDurableExchange("<<default>>", "direct", find(Exchange.NAME, "<<default>>", exchanges));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE);
+ assertEquals("Unexpected number of queues", 2, queues.size());
+ Map<String, Object> queue = find(Queue.NAME, "queue", queues);
+ Map<String, Object> ping = find(Queue.NAME, "ping", queues);
+ Asserts.assertQueue("queue", "standard", queue);
+ Asserts.assertQueue("ping", "standard", ping);
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.FALSE, queue.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.FALSE, ping.get(Queue.DURABLE));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> connections = (List<Map<String, Object>>) hostDetails
+ .get(VIRTUALHOST_CONNECTIONS_ATTRIBUTE);
+ assertEquals("Unexpected number of connections", 1, connections.size());
+ Asserts.assertConnection(connections.get(0), _connection);
+ }
+
+ public void testPutCreateQueue() throws Exception
+ {
+ String queueName = getTestQueueName();
+
+ createQueue(queueName + "-standard", "standard", null);
+
+ Map<String, Object> sortedQueueAttributes = new HashMap<String, Object>();
+ sortedQueueAttributes.put(Queue.SORT_KEY, "sortme");
+ createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes);
+
+ Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>();
+ priorityQueueAttributes.put(Queue.PRIORITIES, 10);
+ createQueue(queueName + "-priority", "priority", priorityQueueAttributes);
+
+ Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>();
+ lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ");
+ createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> standardQueue = find(Queue.NAME, queueName + "-standard" , queues);
+ Map<String, Object> sortedQueue = find(Queue.NAME, queueName + "-sorted" , queues);
+ Map<String, Object> priorityQueue = find(Queue.NAME, queueName + "-priority" , queues);
+ Map<String, Object> lvqQueue = find(Queue.NAME, queueName + "-lvq" , queues);
+
+ Asserts.assertQueue(queueName + "-standard", "standard", standardQueue);
+ Asserts.assertQueue(queueName + "-sorted", "sorted", sortedQueue);
+ Asserts.assertQueue(queueName + "-priority", "priority", priorityQueue);
+ Asserts.assertQueue(queueName + "-lvq", "lvq", lvqQueue);
+
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, standardQueue.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, sortedQueue.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, priorityQueue.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, lvqQueue.get(Queue.DURABLE));
+
+ assertEquals("Unexpected sorted key attribute", "sortme", sortedQueue.get(Queue.SORT_KEY));
+ assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(Queue.LVQ_KEY));
+ assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(Queue.PRIORITIES));
+ }
+
+ public void testPutCreateExchange() throws Exception
+ {
+ String exchangeName = getTestName();
+
+ createExchange(exchangeName + "-direct", "direct");
+ createExchange(exchangeName + "-topic", "topic");
+ createExchange(exchangeName + "-headers", "headers");
+ createExchange(exchangeName + "-fanout", "fanout");
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE);
+ Map<String, Object> directExchange = find(Queue.NAME, exchangeName + "-direct" , exchanges);
+ Map<String, Object> topicExchange = find(Queue.NAME, exchangeName + "-topic" , exchanges);
+ Map<String, Object> headersExchange = find(Queue.NAME, exchangeName + "-headers" , exchanges);
+ Map<String, Object> fanoutExchange = find(Queue.NAME, exchangeName + "-fanout" , exchanges);
+
+ Asserts.assertDurableExchange(exchangeName + "-direct", "direct", directExchange);
+ Asserts.assertDurableExchange(exchangeName + "-topic", "topic", topicExchange);
+ Asserts.assertDurableExchange(exchangeName + "-headers", "headers", headersExchange);
+ Asserts.assertDurableExchange(exchangeName + "-fanout", "fanout", fanoutExchange);
+
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, directExchange.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, topicExchange.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, headersExchange.get(Queue.DURABLE));
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, fanoutExchange.get(Queue.DURABLE));
+
+ }
+
+ public void testPutCreateLVQWithoutKey() throws Exception
+ {
+ String queueName = getTestQueueName()+ "-lvq";
+ createQueue(queueName, "lvq", null);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> lvqQueue = find(Queue.NAME, queueName , queues);
+
+ Asserts.assertQueue(queueName , "lvq", lvqQueue);
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, lvqQueue.get(Queue.DURABLE));
+ assertEquals("Unexpected lvq key attribute", AMQQueueFactory.QPID_LVQ_KEY, lvqQueue.get(Queue.LVQ_KEY));
+ }
+
+ public void testPutCreateSortedQueueWithoutKey() throws Exception
+ {
+ String queueName = getTestQueueName() + "-sorted";
+ int responseCode = tryCreateQueue(queueName, "sorted", null);
+ assertEquals("Unexpected response code", 409, responseCode);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> testQueue = find(Queue.NAME, queueName , queues);
+
+ assertNull("Sorted queue without a key was created ", testQueue);
+ }
+
+ public void testPutCreatePriorityQueueWithoutKey() throws Exception
+ {
+ String queueName = getTestQueueName()+ "-priority";
+ createQueue(queueName, "priority", null);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> priorityQueue = find(Queue.NAME, queueName , queues);
+
+ Asserts.assertQueue(queueName , "priority", priorityQueue);
+ assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, priorityQueue.get(Queue.DURABLE));
+ assertEquals("Unexpected number of priorities", 10, priorityQueue.get(Queue.PRIORITIES));
+ }
+
+ public void testPutCreateStandardQueueWithoutType() throws Exception
+ {
+ String queueName = getTestQueueName();
+ createQueue(queueName, null, null);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> queue = find(Queue.NAME, queueName , queues);
+
+ Asserts.assertQueue(queueName , "standard", queue);
+ }
+
+ public void testPutCreateQueueOfUnsupportedType() throws Exception
+ {
+ String queueName = getTestQueueName();
+ int responseCode = tryCreateQueue(queueName, "unsupported", null);
+ assertEquals("Unexpected response code", 409, responseCode);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> queue = find(Queue.NAME, queueName , queues);
+
+ assertNull("Queue of unsupported type was created", queue);
+ }
+
+ public void testDeleteQueue() throws Exception
+ {
+ String queueName = getTestQueueName();
+ createQueue(queueName, null, null);
+
+ HttpURLConnection connection = openManagementConection("/rest/queue/test/" + queueName, "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> queues = getJsonAsList("/rest/queue/test/" + queueName);
+ assertEquals("Queue should be deleted", 0, queues.size());
+ }
+
+ public void testDeleteQueueById() throws Exception
+ {
+ String queueName = getTestQueueName();
+ createQueue(queueName, null, null);
+ Map<String, Object> queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName);
+
+ HttpURLConnection connection = openManagementConection("/rest/queue/test?id=" + queueDetails.get(Queue.ID), "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> queues = getJsonAsList("/rest/queue/test/" + queueName);
+ assertEquals("Queue should be deleted", 0, queues.size());
+ }
+
+ public void testDeleteExchange() throws Exception
+ {
+ String exchangeName = getTestName();
+ createExchange(exchangeName, "direct");
+
+ HttpURLConnection connection = openManagementConection("/rest/exchange/test/" + exchangeName, "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> queues = getJsonAsList("/rest/exchange/test/" + exchangeName);
+ assertEquals("Exchange should be deleted", 0, queues.size());
+ }
+
+ public void testDeleteExchangeById() throws Exception
+ {
+ String exchangeName = getTestName();
+ createExchange(exchangeName, "direct");
+ Map<String, Object> echangeDetails = getJsonAsSingletonList("/rest/exchange/test/" + exchangeName);
+
+ HttpURLConnection connection = openManagementConection("/rest/exchange/test?id=" + echangeDetails.get(Exchange.ID), "DELETE");
+ connection.connect();
+ assertEquals("Unexpected response code", 200, connection.getResponseCode());
+ List<Map<String, Object>> queues = getJsonAsList("/rest/exchange/test/" + exchangeName);
+ assertEquals("Exchange should be deleted", 0, queues.size());
+ }
+
+ public void testPutCreateQueueWithAttributes() throws Exception
+ {
+ String queueName = getTestQueueName();
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(Queue.ALERT_REPEAT_GAP, 1000);
+ attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_AGE, 3600000);
+ attributes.put(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 1000000000);
+ attributes.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 800);
+ attributes.put(Queue.MAXIMUM_DELIVERY_ATTEMPTS, 15);
+ attributes.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, 2000000000);
+ attributes.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, 1500000000);
+
+ createQueue(queueName + "-standard", "standard", attributes);
+
+ Map<String, Object> sortedQueueAttributes = new HashMap<String, Object>();
+ sortedQueueAttributes.putAll(attributes);
+ sortedQueueAttributes.put(Queue.SORT_KEY, "sortme");
+ createQueue(queueName + "-sorted", "sorted", sortedQueueAttributes);
+
+ Map<String, Object> priorityQueueAttributes = new HashMap<String, Object>();
+ priorityQueueAttributes.putAll(attributes);
+ priorityQueueAttributes.put(Queue.PRIORITIES, 10);
+ createQueue(queueName + "-priority", "priority", priorityQueueAttributes);
+
+ Map<String, Object> lvqQueueAttributes = new HashMap<String, Object>();
+ lvqQueueAttributes.putAll(attributes);
+ lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ");
+ createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes);
+
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ Map<String, Object> standardQueue = find(Queue.NAME, queueName + "-standard" , queues);
+ Map<String, Object> sortedQueue = find(Queue.NAME, queueName + "-sorted" , queues);
+ Map<String, Object> priorityQueue = find(Queue.NAME, queueName + "-priority" , queues);
+ Map<String, Object> lvqQueue = find(Queue.NAME, queueName + "-lvq" , queues);
+
+ attributes.put(Queue.DURABLE, Boolean.TRUE);
+ Asserts.assertQueue(queueName + "-standard", "standard", standardQueue, attributes);
+ Asserts.assertQueue(queueName + "-sorted", "sorted", sortedQueue, attributes);
+ Asserts.assertQueue(queueName + "-priority", "priority", priorityQueue, attributes);
+ Asserts.assertQueue(queueName + "-lvq", "lvq", lvqQueue, attributes);
+
+ assertEquals("Unexpected sorted key attribute", "sortme", sortedQueue.get(Queue.SORT_KEY));
+ assertEquals("Unexpected lvq key attribute", "LVQ", lvqQueue.get(Queue.LVQ_KEY));
+ assertEquals("Unexpected priorities key attribute", 10, priorityQueue.get(Queue.PRIORITIES));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testCreateQueueWithDLQEnabled() throws Exception
+ {
+ String queueName = getTestQueueName();
+
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(AMQQueueFactory.X_QPID_DLQ_ENABLED, true);
+
+ //verify the starting state
+ Map<String, Object> hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+ List<Map<String, Object>> queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ List<Map<String, Object>> exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE);
+
+ assertNull("queue should not have already been present", find(Queue.NAME, queueName , queues));
+ assertNull("queue should not have already been present", find(Queue.NAME, queueName + "_DLQ" , queues));
+ assertNull("exchange should not have already been present", find(Exchange.NAME, queueName + "_DLE" , exchanges));
+
+ //create the queue
+ createQueue(queueName, "standard", attributes);
+
+ //verify the new queue, as well as the DLQueue and DLExchange have been created
+ hostDetails = getJsonAsSingletonList("/rest/virtualhost/test");
+ queues = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE);
+ exchanges = (List<Map<String, Object>>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE);
+
+ Map<String, Object> queue = find(Queue.NAME, queueName , queues);
+ Map<String, Object> dlqQueue = find(Queue.NAME, queueName + "_DLQ" , queues);
+ Map<String, Object> dlExchange = find(Exchange.NAME, queueName + "_DLE" , exchanges);
+ assertNotNull("queue should not have been present", queue);
+ assertNotNull("queue should not have been present", dlqQueue);
+ assertNotNull("exchange should not have been present", dlExchange);
+
+ //verify that the alternate exchange is set as expected on the new queue
+ Map<String, Object> queueAttributes = new HashMap<String, Object>();
+ queueAttributes.put(Queue.ALTERNATE_EXCHANGE, queueName + "_DLE");
+
+ Asserts.assertQueue(queueName, "standard", queue, queueAttributes);
+ Asserts.assertQueue(queueName, "standard", queue, null);
+ }
+
+ private void createExchange(String exchangeName, String exchangeType) throws IOException
+ {
+ HttpURLConnection connection = openManagementConection("/rest/exchange/test/" + exchangeName, "PUT");
+
+ Map<String, Object> queueData = new HashMap<String, Object>();
+ queueData.put(Exchange.NAME, exchangeName);
+ queueData.put(Exchange.DURABLE, Boolean.TRUE);
+ queueData.put(Exchange.TYPE, exchangeType);
+
+ writeJsonRequest(connection, queueData);
+ assertEquals("Unexpected response code", 201, connection.getResponseCode());
+
+ connection.disconnect();
+ }
+
+ private void createQueue(String queueName, String queueType, Map<String, Object> attributes) throws IOException,
+ JsonGenerationException, JsonMappingException
+ {
+ int responseCode = tryCreateQueue(queueName, queueType, attributes);
+ assertEquals("Unexpected response code", 201, responseCode);
+ }
+
+ private int tryCreateQueue(String queueName, String queueType, Map<String, Object> attributes) throws IOException,
+ JsonGenerationException, JsonMappingException
+ {
+ HttpURLConnection connection = openManagementConection("/rest/queue/test/" + queueName, "PUT");
+
+ Map<String, Object> queueData = new HashMap<String, Object>();
+ queueData.put(Queue.NAME, queueName);
+ queueData.put(Queue.DURABLE, Boolean.TRUE);
+ if (queueType != null)
+ {
+ queueData.put(Queue.TYPE, queueType);
+ }
+ if (attributes != null)
+ {
+ queueData.putAll(attributes);
+ }
+
+ writeJsonRequest(connection, queueData);
+ int responseCode = connection.getResponseCode();
+ connection.disconnect();
+ return responseCode;
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/MANIFEST.MF b/java/broker-plugins/management-jmx/MANIFEST.MF
new file mode 100644
index 0000000000..b18ec1ace7
--- /dev/null
+++ b/java/broker-plugins/management-jmx/MANIFEST.MF
@@ -0,0 +1,66 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Qpid Broker-Plugins Management JMX
+Bundle-SymbolicName: broker-plugins-management-jmx
+Bundle-Description: JMX management plugin for Qpid.
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Bundle-Version: 1.0.0
+Bundle-Activator: org.apache.qpid.server.jmx.JMXActivator
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: .
+Bundle-ActivationPolicy: lazy
+Import-Package: org.apache.qpid,
+ org.apache.qpid.framing,
+ org.apache.qpid.protocol,
+ org.apache.qpid.common,
+ org.apache.qpid.management.common.mbeans,
+ org.apache.qpid.management.common.mbeans.annotations,
+ org.apache.qpid.server.security.auth,
+ org.apache.qpid.server.security.auth.manager,
+ org.apache.qpid.server.security.auth.rmi,
+ org.apache.qpid.server.security.auth.sasl,
+ org.apache.qpid.server.binding,
+ org.apache.qpid.server.exchange,
+ org.apache.qpid.server.logging,
+ org.apache.qpid.server.logging.log4j,
+ org.apache.qpid.server.logging.actors,
+ org.apache.qpid.server.logging.messages,
+ org.apache.qpid.server.message,
+ org.apache.qpid.server.model,
+ org.apache.qpid.server.model.adapter,
+ org.apache.qpid.server.model.impl,
+ org.apache.qpid.server.configuration,
+ org.apache.qpid.server.configuration.plugins,
+ org.apache.qpid.server.connection,
+ org.apache.qpid.server.plugins,
+ org.apache.qpid.server.protocol,
+ org.apache.qpid.server.queue,
+ org.apache.qpid.server.registry,
+ org.apache.qpid.server.security,
+ org.apache.qpid.server.security.access,
+ org.apache.qpid.server.stats,
+ org.apache.qpid.server.virtualhost,
+ org.apache.qpid.util,
+ org.apache.commons.codec;version=1.3.0,
+ org.apache.commons.codec.binary;version=1.3.0,
+ org.apache.commons.configuration;version=1.0.0,
+ org.apache.commons.lang;version=1.0.0,
+ org.apache.commons.lang.builder;version=1.0.0,
+ org.apache.commons.lang.time;version=1.0.0,
+ org.apache.log4j;version=1.2.16,
+ org.codehaus.jackson;version=1.9.0,
+ org.codehaus.jackson.map;version=1.9.0,
+ javax.management.remote.rmi,
+ javax.management.remote,
+ javax.servlet,
+ javax.servlet.http,
+ javax.management;version=1.0.0,
+ javax.management.monitor;version=1.0.0,
+ javax.management.openmbean;version=1.0.0,
+ javax.security.auth.login;version=1.0.0,
+ javax.security.auth;version=1.0.0,
+ javax.rmi.ssl;version=1.0.0,
+ org.osgi.util.tracker;version=1.0.0,
+ org.osgi.framework;version=1.3
+Export-Package: org.apache.qpid.server.jmx;uses:="org.osgi.framework"
diff --git a/java/broker-plugins/management-jmx/build.xml b/java/broker-plugins/management-jmx/build.xml
new file mode 100644
index 0000000000..fa50b8467d
--- /dev/null
+++ b/java/broker-plugins/management-jmx/build.xml
@@ -0,0 +1,47 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -->
+<project name="Qpid Broker-Plugins Management JMX" default="build">
+
+ <condition property="systests.optional.depends" value="bdbstore" else="">
+ <or>
+ <and>
+ <contains string="${modules.opt}" substring="bdbstore"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ <and>
+ <istrue value="${optional}"/>
+ <contains string="${profile}" substring="bdb"/>
+ </and>
+ </or>
+ </condition>
+
+ <property name="module.depends" value="common broker management/common" />
+ <property name="module.test.depends" value="systests test broker/test common/test management/common client ${systests.optional.depends}" />
+
+ <property name="module.manifest" value="MANIFEST.MF" />
+ <property name="module.plugin" value="true" />
+ <property name="module.genpom" value="true"/>
+ <property name="module.genpom.args" value="-Sqpid-common=provided -Sqpid-broker=provided -Sqpid-management-common=provided"/>
+
+ <property name="broker-plugins-management-jmx.libs" value=""/>
+
+ <import file="../../module.xml" />
+
+ <target name="bundle" depends="bundle-tasks" />
+</project>
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java
new file mode 100644
index 0000000000..5c39a0c26a
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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.jmx;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+
+/**
+ * This class provides additional feature of Notification Broadcaster to the
+ * DefaultManagedObject.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public abstract class AMQManagedObject extends DefaultManagedObject
+ implements NotificationBroadcaster
+{
+ private final NotificationBroadcasterSupport _broadcaster = new NotificationBroadcasterSupport();
+
+ private AtomicLong _notificationSequenceNumber = new AtomicLong();
+
+ protected AMQManagedObject(Class<?> managementInterface, String typeName, ManagedObjectRegistry registry)
+ throws NotCompliantMBeanException
+ {
+ super(managementInterface, typeName, registry);
+ // CurrentActor will be defined as these objects are created during
+ // broker startup.
+
+ }
+
+ // notification broadcaster implementation
+
+ public void addNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ {
+ _broadcaster.addNotificationListener(listener, filter, handback);
+ }
+
+ public void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException
+ {
+ _broadcaster.removeNotificationListener(listener);
+ }
+
+
+ /**
+ * broadcaster support class
+ */
+ protected NotificationBroadcasterSupport getBroadcaster()
+ {
+ return _broadcaster;
+ }
+
+ protected long incrementAndGetSequenceNumber()
+ {
+ return _notificationSequenceNumber.incrementAndGet();
+ }
+
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java
new file mode 100644
index 0000000000..4446f96802
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.log4j.Logger;
+
+import javax.management.JMException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+/**
+ * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful
+ * to extend this class rather than implementing ManagedObject from scratch.
+ *
+ */
+public abstract class DefaultManagedObject extends StandardMBean implements ManagedObject
+{
+ private static final Logger LOGGER = Logger.getLogger(DefaultManagedObject.class);
+
+ private final Class<?> _managementInterface;
+
+ private final String _typeName;
+
+ private final MBeanInfo _mbeanInfo;
+
+ private ManagedObjectRegistry _registry;
+
+ protected DefaultManagedObject(Class<?> managementInterface, String typeName, ManagedObjectRegistry registry)
+ throws NotCompliantMBeanException
+ {
+ super(managementInterface);
+ _registry = registry;
+ _managementInterface = managementInterface;
+ _typeName = typeName;
+ _mbeanInfo = buildMBeanInfo();
+ }
+
+ public ManagedObjectRegistry getRegistry()
+ {
+ return _registry;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo()
+ {
+ return _mbeanInfo;
+ }
+
+ public String getType()
+ {
+ return _typeName;
+ }
+
+ public Class<?> getManagementInterface()
+ {
+ return _managementInterface;
+ }
+
+ public abstract ManagedObject getParentObject();
+
+
+ public void register() throws JMException
+ {
+ _registry.registerObject(this);
+ }
+
+ public void unregister() throws JMException
+ {
+ try
+ {
+ if(_registry != null)
+ {
+ _registry.unregisterObject(this);
+ }
+ }
+ finally
+ {
+ _registry = null;
+ }
+ }
+
+ public String toString()
+ {
+ return getObjectInstanceName() + "[" + getType() + "]";
+ }
+
+ /**
+ * Created the ObjectName as per the JMX Specs
+ * @return ObjectName
+ * @throws javax.management.MalformedObjectNameException
+ */
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ String name = getObjectInstanceName();
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+
+ objectName.append(":type=");
+ objectName.append(getHierarchicalType(this));
+
+ objectName.append(",");
+ objectName.append(getHierarchicalName(this));
+ objectName.append("name=").append(name);
+
+ return new ObjectName(objectName.toString());
+ }
+
+ protected ObjectName getObjectNameForSingleInstanceMBean() throws MalformedObjectNameException
+ {
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+
+ objectName.append(":type=");
+ objectName.append(getHierarchicalType(this));
+
+ String hierarchyName = getHierarchicalName(this);
+ if (hierarchyName != null)
+ {
+ objectName.append(",");
+ objectName.append(hierarchyName.substring(0, hierarchyName.lastIndexOf(",")));
+ }
+
+ return new ObjectName(objectName.toString());
+ }
+
+ protected String getHierarchicalType(ManagedObject obj)
+ {
+ if (obj.getParentObject() != null)
+ {
+ String parentType = getHierarchicalType(obj.getParentObject()).toString();
+ return parentType + "." + obj.getType();
+ }
+ else
+ {
+ return obj.getType();
+ }
+ }
+
+ protected String getHierarchicalName(ManagedObject obj)
+ {
+ if (obj.getParentObject() != null)
+ {
+ String parentName = obj.getParentObject().getType() + "=" +
+ obj.getParentObject().getObjectInstanceName() + ","+
+ getHierarchicalName(obj.getParentObject());
+
+ return parentName;
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ private MBeanInfo buildMBeanInfo() throws NotCompliantMBeanException
+ {
+ return new MBeanInfo(this.getClass().getName(),
+ MBeanIntrospector.getMBeanDescription(this.getClass()),
+ MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()),
+ MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()),
+ MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()),
+ this.getNotificationInfo());
+ }
+
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ return null;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java
new file mode 100644
index 0000000000..c588b40de7
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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.jmx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class JMXActivator implements BundleActivator
+{
+ private static final Logger LOGGER = Logger.getLogger(JMXActivator.class);
+
+ private String _bundleName;
+ private JMXService _jmxService;
+
+ private List<ServiceRegistration> _registeredServices;
+
+
+ public void start(final BundleContext ctx) throws Exception
+ {
+ boolean jmxManagementEnabled = ApplicationRegistry.getInstance().getConfiguration().getJMXManagementEnabled();
+
+ if (jmxManagementEnabled)
+ {
+ _jmxService = new JMXService();
+ startJmsService(_jmxService);
+
+ _bundleName = ctx.getBundle().getSymbolicName();
+
+ _registeredServices = registerServices(ctx);
+ }
+ else
+ {
+ LOGGER.debug("Skipping registration of JMX plugin as JMX Management disabled in config. ");
+ }
+ }
+
+ public void stop(final BundleContext bundleContext) throws Exception
+ {
+ try
+ {
+ if (_jmxService != null)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Stopping jmx plugin: " + _bundleName);
+ }
+ _jmxService.close();
+ }
+
+ if (_registeredServices != null)
+ {
+ unregisterServices();
+ }
+ }
+ finally
+ {
+ _jmxService = null;
+ _registeredServices = null;
+ }
+ }
+
+
+ private List<ServiceRegistration> registerServices(BundleContext ctx)
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Registering jmx plugin: " + _bundleName);
+ }
+
+ List<ServiceRegistration> serviceRegistrations = new ArrayList<ServiceRegistration>();
+
+ ServiceRegistration jmxServiceRegistration = ctx.registerService(JMXService.class.getName(), _jmxService, null);
+ ServiceRegistration jmxConfigFactoryRegistration = ctx.registerService(ConfigurationPluginFactory.class.getName(), JMXConfiguration.FACTORY, null);
+
+ serviceRegistrations.add(jmxServiceRegistration);
+ serviceRegistrations.add(jmxConfigFactoryRegistration);
+ return serviceRegistrations;
+ }
+
+ private void startJmsService(JMXService jmxService) throws Exception
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Starting JMX service");
+ }
+ boolean startedSuccessfully = false;
+ try
+ {
+ jmxService.start();
+ startedSuccessfully = true;
+ }
+ finally
+ {
+ if (!startedSuccessfully)
+ {
+ LOGGER.error("JMX failed to start normally, closing service");
+ jmxService.close();
+ }
+ }
+ }
+
+ private void unregisterServices()
+ {
+ for (Iterator<ServiceRegistration> iterator = _registeredServices.iterator(); iterator.hasNext();)
+ {
+ ServiceRegistration service = iterator.next();
+ service.unregister();
+ }
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java
new file mode 100644
index 0000000000..dc9a712f90
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class JMXConfiguration extends ConfigurationPlugin
+{
+ CompositeConfiguration _finalConfig;
+
+ public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory()
+ {
+ public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException
+ {
+ ConfigurationPlugin instance = new JMXConfiguration();
+ instance.setConfiguration(path, config);
+ return instance;
+ }
+
+ public List<String> getParentPaths()
+ {
+ return Arrays.asList("jmx");
+ }
+ };
+
+ public String[] getElementsProcessed()
+ {
+ return new String[] { "" };
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _finalConfig;
+ }
+
+
+ @Override
+ public void validateConfiguration() throws ConfigurationException
+ {
+ // Valid Configuration either has xml links to new files
+ _finalConfig = new CompositeConfiguration(getConfig());
+ List subFiles = getConfig().getList("xml[@fileName]");
+ for (Object subFile : subFiles)
+ {
+ _finalConfig.addConfiguration(new XMLConfiguration((String) subFile));
+ }
+
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java
new file mode 100644
index 0000000000..0648235077
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java
@@ -0,0 +1,499 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+
+import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationFilterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.MBeanServerForwarder;
+import javax.management.remote.rmi.RMIConnection;
+import javax.management.remote.rmi.RMIConnectorServer;
+import javax.management.remote.rmi.RMIJRMPServerImpl;
+import javax.management.remote.rmi.RMIServerImpl;
+import javax.rmi.ssl.SslRMIClientSocketFactory;
+import javax.rmi.ssl.SslRMIServerSocketFactory;
+import javax.security.auth.Subject;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Proxy;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.rmi.AlreadyBoundException;
+import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no
+ * security features implemented like user authentication and authorisation.
+ */
+public class JMXManagedObjectRegistry implements ManagedObjectRegistry
+{
+ private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class);
+
+ private final MBeanServer _mbeanServer;
+ private JMXConnectorServer _cs;
+ private Registry _rmiRegistry;
+ private boolean _useCustomSocketFactory;
+
+ private final int _jmxPortRegistryServer;
+ private final int _jmxPortConnectorServer;
+
+ public JMXManagedObjectRegistry() throws AMQException
+ {
+ _log.info("Initialising managed object registry using platform MBean server");
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+
+ // Retrieve the config parameters
+ _useCustomSocketFactory = appRegistry.getConfiguration().getUseCustomRMISocketFactory();
+ boolean platformServer = appRegistry.getConfiguration().getPlatformMbeanserver();
+
+ _mbeanServer =
+ platformServer ? ManagementFactory.getPlatformMBeanServer()
+ : MBeanServerFactory.createMBeanServer(ManagedObject.DOMAIN);
+
+ _jmxPortRegistryServer = appRegistry.getConfiguration().getJMXPortRegistryServer();
+ _jmxPortConnectorServer = appRegistry.getConfiguration().getJMXConnectorServerPort();
+
+ }
+
+ public void start() throws IOException, ConfigurationException
+ {
+
+ CurrentActor.get().message(ManagementConsoleMessages.STARTUP());
+
+ //check if system properties are set to use the JVM's out-of-the-box JMXAgent
+ if (areOutOfTheBoxJMXOptionsSet())
+ {
+ CurrentActor.get().message(ManagementConsoleMessages.READY(true));
+ return;
+ }
+
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+
+
+ //Socket factories for the RMIConnectorServer, either default or SLL depending on configuration
+ RMIClientSocketFactory csf;
+ RMIServerSocketFactory ssf;
+
+ //check ssl enabled option in config, default to true if option is not set
+ boolean sslEnabled = appRegistry.getConfiguration().getManagementSSLEnabled();
+
+ if (sslEnabled)
+ {
+ //set the SSL related system properties used by the SSL RMI socket factories to the values
+ //given in the configuration file, unless command line settings have already been specified
+ String keyStorePath;
+
+ if(System.getProperty("javax.net.ssl.keyStore") != null)
+ {
+ keyStorePath = System.getProperty("javax.net.ssl.keyStore");
+ }
+ else
+ {
+ keyStorePath = appRegistry.getConfiguration().getManagementKeyStorePath();
+ }
+
+ //check the keystore path value is valid
+ if (keyStorePath == null)
+ {
+ throw new ConfigurationException("JMX management SSL keystore path not defined, " +
+ "unable to start SSL protected JMX ConnectorServer");
+ }
+ else
+ {
+ //ensure the system property is set
+ System.setProperty("javax.net.ssl.keyStore", keyStorePath);
+
+ //check the file is usable
+ File ksf = new File(keyStorePath);
+
+ if (!ksf.exists())
+ {
+ throw new FileNotFoundException("Cannot find JMX management SSL keystore file: " + ksf);
+ }
+ if (!ksf.canRead())
+ {
+ throw new FileNotFoundException("Cannot read JMX management SSL keystore file: "
+ + ksf + ". Check permissions.");
+ }
+
+ CurrentActor.get().message(ManagementConsoleMessages.SSL_KEYSTORE(ksf.getAbsolutePath()));
+ }
+
+ //check the key store password is set
+ if (System.getProperty("javax.net.ssl.keyStorePassword") == null)
+ {
+
+ if (appRegistry.getConfiguration().getManagementKeyStorePassword() == null)
+ {
+ throw new ConfigurationException("JMX management SSL keystore password not defined, " +
+ "unable to start requested SSL protected JMX server");
+ }
+ else
+ {
+ System.setProperty("javax.net.ssl.keyStorePassword",
+ appRegistry.getConfiguration().getManagementKeyStorePassword());
+ }
+ }
+
+ //create the SSL RMI socket factories
+ csf = new SslRMIClientSocketFactory();
+ ssf = new SslRMIServerSocketFactory();
+ }
+ else
+ {
+ //Do not specify any specific RMI socket factories, resulting in use of the defaults.
+ csf = null;
+ ssf = null;
+ }
+
+ //add a JMXAuthenticator implementation the env map to authenticate the RMI based JMX connector server
+ RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator(new InetSocketAddress(_jmxPortRegistryServer));
+ HashMap<String,Object> env = new HashMap<String,Object>();
+ env.put(JMXConnectorServer.AUTHENTICATOR, rmipa);
+
+ /*
+ * Start a RMI registry on the management port, to hold the JMX RMI ConnectorServer stub.
+ * Using custom socket factory to prevent anyone (including us unfortunately) binding to the registry using RMI.
+ * As a result, only binds made using the object reference will succeed, thus securing it from external change.
+ */
+ System.setProperty("java.rmi.server.randomIDs", "true");
+ if(_useCustomSocketFactory)
+ {
+ _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, new CustomRMIServerSocketFactory());
+ }
+ else
+ {
+ _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, null);
+ }
+
+ CurrentActor.get().message(ManagementConsoleMessages.LISTENING("RMI Registry", _jmxPortRegistryServer));
+
+ /*
+ * We must now create the RMI ConnectorServer manually, as the JMX Factory methods use RMI calls
+ * to bind the ConnectorServer to the registry, which will now fail as for security we have
+ * locked it from any RMI based modifications, including our own. Instead, we will manually bind
+ * the RMIConnectorServer stub to the registry using its object reference, which will still succeed.
+ *
+ * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer
+ * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's.
+ */
+ final Map<String, String> connectionIdUsernameMap = new ConcurrentHashMap<String, String>();
+ final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env)
+ {
+
+ /**
+ * Override makeClient so we can cache the username of the client in a Map keyed by connectionId.
+ * ConnectionId is guaranteed to be unique per client connection, according to the JMS spec.
+ * An instance of NotificationListener (mapCleanupListener) will be responsible for removing these Map
+ * entries.
+ *
+ * @see javax.management.remote.rmi.RMIJRMPServerImpl#makeClient(String, javax.security.auth.Subject)
+ */
+ @Override
+ protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException
+ {
+ final RMIConnection makeClient = super.makeClient(connectionId, subject);
+ final UsernamePrincipal usernamePrincipalFromSubject = UsernamePrincipal.getUsernamePrincipalFromSubject(subject);
+ connectionIdUsernameMap.put(connectionId, usernamePrincipalFromSubject.getName());
+ return makeClient;
+ }
+ };
+
+ // Create a Listener responsible for removing the map entries add by the #makeClient entry above.
+ final NotificationListener mapCleanupListener = new NotificationListener()
+ {
+
+ public void handleNotification(Notification notification, Object handback)
+ {
+ final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
+ connectionIdUsernameMap.remove(connectionId);
+ }
+ };
+
+ String localHost;
+ try
+ {
+ localHost = InetAddress.getLocalHost().getHostName();
+ }
+ catch(UnknownHostException ex)
+ {
+ localHost="127.0.0.1";
+ }
+ final String hostname = localHost;
+ final JMXServiceURL externalUrl = new JMXServiceURL(
+ "service:jmx:rmi://"+hostname+":"+(_jmxPortConnectorServer)+"/jndi/rmi://"+hostname+":"+_jmxPortRegistryServer+"/jmxrmi");
+
+ final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, _jmxPortConnectorServer);
+ _cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer)
+ {
+ @Override
+ public synchronized void start() throws IOException
+ {
+ try
+ {
+ //manually bind the connector server to the registry at key 'jmxrmi', like the out-of-the-box agent
+ _rmiRegistry.bind("jmxrmi", rmiConnectorServerStub);
+ }
+ catch (AlreadyBoundException abe)
+ {
+ //key was already in use. shouldnt happen here as its a new registry, unbindable by normal means.
+
+ //IOExceptions are the only checked type throwable by the method, wrap and rethrow
+ IOException ioe = new IOException(abe.getMessage());
+ ioe.initCause(abe);
+ throw ioe;
+ }
+
+ //now do the normal tasks
+ super.start();
+ }
+
+ @Override
+ public synchronized void stop() throws IOException
+ {
+ try
+ {
+ if (_rmiRegistry != null)
+ {
+ _rmiRegistry.unbind("jmxrmi");
+ }
+ }
+ catch (NotBoundException nbe)
+ {
+ // TODO consider if we want to keep new logging
+ _log.error("Failed to unbind jmxrmi", nbe);
+ //ignore
+ }
+
+ //now do the normal tasks
+ super.stop();
+ }
+
+ @Override
+ public JMXServiceURL getAddress()
+ {
+ //must return our pre-crafted url that includes the full details, inc JNDI details
+ return externalUrl;
+ }
+
+ };
+
+
+ //Add the custom invoker as an MBeanServerForwarder, and start the RMIConnectorServer.
+ MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
+ _cs.setMBeanServerForwarder(mbsf);
+
+
+ // Get the handler that is used by the above MBInvocationHandler Proxy.
+ // which is the MBeanInvocationHandlerImpl and so also a NotificationListener.
+ final NotificationListener invocationHandler = (NotificationListener) Proxy.getInvocationHandler(mbsf);
+
+ // Install a notification listener on OPENED, CLOSED, and FAILED,
+ // passing the map of connection-ids to usernames as hand-back data.
+ final NotificationFilterSupport invocationHandlerFilter = new NotificationFilterSupport();
+ invocationHandlerFilter.enableType(JMXConnectionNotification.OPENED);
+ invocationHandlerFilter.enableType(JMXConnectionNotification.CLOSED);
+ invocationHandlerFilter.enableType(JMXConnectionNotification.FAILED);
+ _cs.addNotificationListener(invocationHandler, invocationHandlerFilter, connectionIdUsernameMap);
+
+ // Install a second notification listener on CLOSED AND FAILED only to remove the entry from the
+ // Map. Here we rely on the fact that JMX will call the listeners in the order in which they are
+ // installed.
+ final NotificationFilterSupport mapCleanupHandlerFilter = new NotificationFilterSupport();
+ mapCleanupHandlerFilter.enableType(JMXConnectionNotification.CLOSED);
+ mapCleanupHandlerFilter.enableType(JMXConnectionNotification.FAILED);
+ _cs.addNotificationListener(mapCleanupListener, mapCleanupHandlerFilter, null);
+
+ _cs.start();
+
+ String connectorServer = (sslEnabled ? "SSL " : "") + "JMX RMIConnectorServer";
+ CurrentActor.get().message(ManagementConsoleMessages.LISTENING(connectorServer, _jmxPortConnectorServer));
+
+ CurrentActor.get().message(ManagementConsoleMessages.READY(false));
+ }
+
+ /*
+ * Custom RMIServerSocketFactory class, used to prevent updates to the RMI registry.
+ * Supplied to the registry at creation, this will prevent RMI-based operations on the
+ * registry such as attempting to bind a new object, thereby securing it from tampering.
+ * This is accomplished by always returning null when attempting to determine the address
+ * of the caller, thus ensuring the registry will refuse the attempt. Calls to bind etc
+ * made using the object reference will not be affected and continue to operate normally.
+ */
+
+ private static class CustomRMIServerSocketFactory implements RMIServerSocketFactory
+ {
+
+ public ServerSocket createServerSocket(int port) throws IOException
+ {
+ return new NoLocalAddressServerSocket(port);
+ }
+
+ private static class NoLocalAddressServerSocket extends ServerSocket
+ {
+ NoLocalAddressServerSocket(int port) throws IOException
+ {
+ super(port);
+ }
+
+ @Override
+ public Socket accept() throws IOException
+ {
+ Socket s = new NoLocalAddressSocket();
+ super.implAccept(s);
+ return s;
+ }
+ }
+
+ private static class NoLocalAddressSocket extends Socket
+ {
+ @Override
+ public InetAddress getInetAddress()
+ {
+ return null;
+ }
+ }
+ }
+
+
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ _mbeanServer.registerMBean(managedObject, managedObject.getObjectName());
+ }
+
+ public void unregisterObject(ManagedObject managedObject) throws JMException
+ {
+ _mbeanServer.unregisterMBean(managedObject.getObjectName());
+ }
+
+ // checks if the system properties are set which enable the JVM's out-of-the-box JMXAgent.
+ private boolean areOutOfTheBoxJMXOptionsSet()
+ {
+ if (System.getProperty("com.sun.management.jmxremote") != null)
+ {
+ return true;
+ }
+
+ if (System.getProperty("com.sun.management.jmxremote.port") != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ //Stops the JMXConnectorServer and RMIRegistry, then unregisters any remaining MBeans from the MBeanServer
+ public void close()
+ {
+ _log.debug("close() called");
+
+ if (_cs != null)
+ {
+ // Stopping the JMX ConnectorServer
+ try
+ {
+ CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("JMX RMIConnectorServer", _cs.getAddress().getPort()));
+ _cs.stop();
+ }
+ catch (IOException e)
+ {
+ _log.error("Exception while closing the JMX ConnectorServer: ", e);
+ }
+ }
+
+ if (_rmiRegistry != null)
+ {
+ // Stopping the RMI registry
+ CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _jmxPortRegistryServer));
+ try
+ {
+ boolean success = UnicastRemoteObject.unexportObject(_rmiRegistry, false);
+ if (!success)
+ {
+ _log.warn("Failed to unexport object " + _rmiRegistry);
+ }
+ }
+ catch (NoSuchObjectException e)
+ {
+ _log.error("Exception while closing the RMI Registry: ", e);
+ }
+ }
+
+ //ObjectName query to gather all Qpid related MBeans
+ ObjectName mbeanNameQuery = null;
+ try
+ {
+ mbeanNameQuery = new ObjectName(ManagedObject.DOMAIN + ":*");
+ }
+ catch (Exception e1)
+ {
+ _log.warn("Unable to generate MBean ObjectName query for close operation");
+ }
+
+ for (ObjectName name : _mbeanServer.queryNames(mbeanNameQuery, null))
+ {
+ try
+ {
+ _mbeanServer.unregisterMBean(name);
+ }
+ catch (JMException e)
+ {
+ _log.error("Exception unregistering MBean '"+ name +"': " + e.getMessage());
+ }
+ }
+
+ CurrentActor.get().message(ManagementConsoleMessages.STOPPED());
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java
new file mode 100644
index 0000000000..7a232d2584
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java
@@ -0,0 +1,193 @@
+/*
+ *
+ * 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.jmx;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import javax.management.JMException;
+import javax.management.StandardMBean;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBean;
+import org.apache.qpid.server.jmx.mbeans.UserManagementMBean;
+import org.apache.qpid.server.jmx.mbeans.ConfigurationManagementMBean;
+import org.apache.qpid.server.jmx.mbeans.ServerInformationMBean;
+import org.apache.qpid.server.jmx.mbeans.Shutdown;
+import org.apache.qpid.server.jmx.mbeans.VirtualHostMBean;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfigurationChangeListener;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+
+public class JMXService implements ConfigurationChangeListener
+{
+ private static final ClassLoader BUNDLE_CLASSLOADER = JMXService.class.getClassLoader();
+
+ private static final Logger LOGGER = Logger.getLogger(JMXService.class);
+
+ private final Broker _broker;
+ private final JMXManagedObjectRegistry _objectRegistry;
+ private final Shutdown _shutdown;
+ private final ServerInformationMBean _serverInfo;
+ private final ConfigurationManagementMBean _configManagement;
+ private final LoggingManagementMBean _loggingManagement;
+
+ private final Map<ConfiguredObject, AMQManagedObject> _children = new HashMap<ConfiguredObject, AMQManagedObject>();
+
+ public JMXService() throws AMQException, JMException
+ {
+ _broker = ApplicationRegistry.getInstance().getBroker();
+ _objectRegistry = new JMXManagedObjectRegistry();
+
+ _broker.addChangeListener(this);
+ synchronized (_children)
+ {
+ for(VirtualHost virtualHost : _broker.getVirtualHosts())
+ {
+ if(!_children.containsKey(virtualHost))
+ {
+ _children.put(virtualHost, new VirtualHostMBean(virtualHost, _objectRegistry));
+ }
+ }
+ }
+ _shutdown = new Shutdown(_objectRegistry);
+ _serverInfo = new ServerInformationMBean(_objectRegistry, _broker);
+ _configManagement = new ConfigurationManagementMBean(_objectRegistry);
+ _loggingManagement = new LoggingManagementMBean(LoggingFacade.getCurrentInstance(), _objectRegistry);
+ }
+
+ public void start() throws IOException, ConfigurationException
+ {
+ _objectRegistry.start();
+ }
+
+ public void close()
+ {
+ _broker.removeChangeListener(this);
+
+ _objectRegistry.close();
+ }
+
+ public void stateChanged(ConfiguredObject object, State oldState, State newState)
+ {
+
+ }
+
+ public void childAdded(ConfiguredObject object, ConfiguredObject child)
+ {
+ synchronized (_children)
+ {
+ try
+ {
+ AMQManagedObject mbean;
+ if(child instanceof VirtualHost)
+ {
+ VirtualHost vhostChild = (VirtualHost)child;
+ mbean = new VirtualHostMBean(vhostChild, _objectRegistry);
+ }
+ else if(child instanceof PasswordCredentialManagingAuthenticationProvider)
+ {
+ mbean = new UserManagementMBean((PasswordCredentialManagingAuthenticationProvider) child, _objectRegistry);
+ }
+ else
+ {
+ mbean = null;
+ }
+
+ if (mbean != null)
+ {
+ createAdditionalMBeansFromProviders(child, mbean);
+ }
+ }
+ catch(JMException e)
+ {
+ LOGGER.error("Error creating mbean", e);
+ // TODO - Implement error reporting on mbean creation
+ }
+ }
+ }
+
+
+ public void childRemoved(ConfiguredObject object, ConfiguredObject child)
+ {
+ // TODO - implement vhost removal (possibly just removing the instanceof check below)
+
+ synchronized (_children)
+ {
+ if(child instanceof PasswordCredentialManagingAuthenticationProvider)
+ {
+ AMQManagedObject mbean = _children.remove(child);
+ if(mbean != null)
+ {
+ try
+ {
+ mbean.unregister();
+ }
+ catch(JMException e)
+ {
+ LOGGER.error("Error creating mbean", e);
+ //TODO - report error on removing child MBean
+ }
+ }
+ }
+
+ }
+ }
+
+ private void createAdditionalMBeansFromProviders(ConfiguredObject child, AMQManagedObject mbean) throws JMException
+ {
+ _children.put(child, mbean);
+
+ for (Iterator<MBeanProvider> iterator = getMBeanProviderIterator(); iterator.hasNext();)
+ {
+ MBeanProvider provider = iterator.next();
+ LOGGER.debug("Consulting mbean provider : " + provider + " for child : " + child);
+ if (provider.isChildManageableByMBean(child))
+ {
+ LOGGER.debug("Provider will create mbean ");
+ StandardMBean bean = provider.createMBean(child, mbean);
+ // TODO track the mbeans that have been created on behalf of a child in a map, then
+ // if the child is ever removed, destroy these beans too.
+ }
+ }
+ }
+
+ /**
+ * Finds all classes implementing the {@link MBeanProvider} interface. This will find
+ * <b>only</b> those classes which are visible to the classloader of this OSGI bundle.
+ */
+ private Iterator<MBeanProvider> getMBeanProviderIterator()
+ {
+ return ServiceLoader.load(MBeanProvider.class, BUNDLE_CLASSLOADER).iterator();
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java
new file mode 100644
index 0000000000..79ddc8cfc0
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java
@@ -0,0 +1,400 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.NotCompliantMBeanException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is a utility class to introspect the MBean class and the management
+ * interface class for various purposes.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+class MBeanIntrospector
+{
+
+ private static final String _defaultAttributeDescription = "Management attribute";
+ private static final String _defaultOerationDescription = "Management operation";
+ private static final String _defaultConstructorDescription = "MBean constructor";
+ private static final String _defaultMbeanDescription = "Management interface of the MBean";
+
+ private MBeanIntrospector()
+ {
+ }
+
+ /**
+ * Introspects the management interface class for MBean attributes.
+ * @param interfaceClass
+ * @return MBeanAttributeInfo[]
+ * @throws javax.management.NotCompliantMBeanException
+ */
+ static MBeanAttributeInfo[] getMBeanAttributesInfo(Class interfaceClass)
+ throws NotCompliantMBeanException
+ {
+ List<MBeanAttributeInfo> attributesList = new ArrayList<MBeanAttributeInfo>();
+
+ /**
+ * Using reflection, all methods of the managemetn interface will be analysed,
+ * and MBeanInfo will be created.
+ */
+ for (Method method : interfaceClass.getMethods())
+ {
+ String name = method.getName();
+ Class<?> resultType = method.getReturnType();
+ MBeanAttributeInfo attributeInfo = null;
+
+ if (isAttributeGetterMethod(method))
+ {
+ String desc = getAttributeDescription(method);
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ resultType.getName(),
+ desc,
+ true,
+ false,
+ false);
+ int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
+ if (index == -1)
+ {
+ attributesList.add(attributeInfo);
+ }
+ else
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ resultType.getName(),
+ desc,
+ true,
+ true,
+ false);
+ attributesList.set(index, attributeInfo);
+ }
+ }
+ else if (isAttributeSetterMethod(method))
+ {
+ String desc = getAttributeDescription(method);
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ method.getParameterTypes()[0].getName(),
+ desc,
+ false,
+ true,
+ false);
+ int index = getIndexIfAlreadyExists(attributeInfo, attributesList);
+ if (index == -1)
+ {
+ attributesList.add(attributeInfo);
+ }
+ else
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(3),
+ method.getParameterTypes()[0].getName(),
+ desc,
+ true,
+ true,
+ false);
+ attributesList.set(index, attributeInfo);
+ }
+ }
+ else if (isAttributeBoolean(method))
+ {
+ attributeInfo = new MBeanAttributeInfo(name.substring(2),
+ resultType.getName(),
+ getAttributeDescription(method),
+ true,
+ false,
+ true);
+ attributesList.add(attributeInfo);
+ }
+ }
+
+ return attributesList.toArray(new MBeanAttributeInfo[0]);
+ }
+
+ /**
+ * Introspects the management interface class for management operations.
+ * @param interfaceClass
+ * @return MBeanOperationInfo[]
+ */
+ static MBeanOperationInfo[] getMBeanOperationsInfo(Class interfaceClass)
+ {
+ List<MBeanOperationInfo> operationsList = new ArrayList<MBeanOperationInfo>();
+
+ for (Method method : interfaceClass.getMethods())
+ {
+ if (!isAttributeGetterMethod(method) &&
+ !isAttributeSetterMethod(method) &&
+ !isAttributeBoolean(method))
+ {
+ operationsList.add(getOperationInfo(method));
+ }
+ }
+
+ return operationsList.toArray(new MBeanOperationInfo[0]);
+ }
+
+ /**
+ * Checks if the method is an attribute getter method.
+ * @param method
+ * @return true if the method is an attribute getter method.
+ */
+ private static boolean isAttributeGetterMethod(Method method)
+ {
+ if (!(method.getName().equals("get")) &&
+ method.getName().startsWith("get") &&
+ method.getParameterTypes().length == 0 &&
+ !method.getReturnType().equals(void.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the method is an attribute setter method.
+ * @param method
+ * @return true if the method is an attribute setter method.
+ */
+ private static boolean isAttributeSetterMethod(Method method)
+ {
+ if (!(method.getName().equals("set")) &&
+ method.getName().startsWith("set") &&
+ method.getParameterTypes().length == 1 &&
+ method.getReturnType().equals(void.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the attribute is a boolean and the method is a isX kind og method.
+ * @param method
+ * @return true if the method is an attribute isX type of method
+ */
+ private static boolean isAttributeBoolean(Method method)
+ {
+ if (!(method.getName().equals("is")) &&
+ method.getName().startsWith("is") &&
+ method.getParameterTypes().length == 0 &&
+ method.getReturnType().equals(boolean.class))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper method to retrieve the attribute index from the list of attributes.
+ * @param attribute
+ * @param list
+ * @return attribute index no. -1 if attribtue doesn't exist
+ * @throws javax.management.NotCompliantMBeanException
+ */
+ private static int getIndexIfAlreadyExists(MBeanAttributeInfo attribute,
+ List<MBeanAttributeInfo> list)
+ throws NotCompliantMBeanException
+ {
+ String exceptionMsg = "Conflicting attribute methods for attribute " + attribute.getName();
+
+ for (MBeanAttributeInfo memberAttribute : list)
+ {
+ if (attribute.getName().equals(memberAttribute.getName()))
+ {
+ if (!attribute.getType().equals(memberAttribute.getType()))
+ {
+ throw new NotCompliantMBeanException(exceptionMsg);
+ }
+ if (attribute.isReadable() && memberAttribute.isReadable())
+ {
+ if (attribute.isIs() != memberAttribute.isIs())
+ {
+ throw new NotCompliantMBeanException(exceptionMsg);
+ }
+ }
+
+ return list.indexOf(memberAttribute);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Retrieves the attribute description from annotation
+ * @param attributeMethod
+ * @return attribute description
+ */
+ private static String getAttributeDescription(Method attributeMethod)
+ {
+ MBeanAttribute anno = attributeMethod.getAnnotation(MBeanAttribute.class);
+ if (anno != null)
+ {
+ return anno.description();
+ }
+ return _defaultAttributeDescription;
+ }
+
+ /**
+ * Introspects the method to retrieve the operation information.
+ * @param operation
+ * @return MBeanOperationInfo
+ */
+ private static MBeanOperationInfo getOperationInfo(Method operation)
+ {
+ MBeanOperationInfo operationInfo = null;
+ Class<?> returnType = operation.getReturnType();
+
+ MBeanParameterInfo[] paramsInfo = getParametersInfo(operation.getParameterAnnotations(),
+ operation.getParameterTypes());
+
+ String operationDesc = _defaultOerationDescription;
+ int impact = MBeanOperationInfo.UNKNOWN;
+
+ if (operation.getAnnotation(MBeanOperation.class) != null)
+ {
+ operationDesc = operation.getAnnotation(MBeanOperation.class).description();
+ impact = operation.getAnnotation(MBeanOperation.class).impact();
+ }
+ operationInfo = new MBeanOperationInfo(operation.getName(),
+ operationDesc,
+ paramsInfo,
+ returnType.getName(),
+ impact);
+
+ return operationInfo;
+ }
+
+ /**
+ * Constructs the parameter info.
+ * @param paramsAnno
+ * @param paramTypes
+ * @return MBeanParameterInfo[]
+ */
+ private static MBeanParameterInfo[] getParametersInfo(Annotation[][] paramsAnno,
+ Class<?>[] paramTypes)
+ {
+ int noOfParams = paramsAnno.length;
+
+ MBeanParameterInfo[] paramsInfo = new MBeanParameterInfo[noOfParams];
+
+ for (int i = 0; i < noOfParams; i++)
+ {
+ MBeanParameterInfo paramInfo = null;
+ String type = paramTypes[i].getName();
+ for (Annotation anno : paramsAnno[i])
+ {
+ String name,desc;
+ if (MBeanOperationParameter.class.isInstance(anno))
+ {
+ name = MBeanOperationParameter.class.cast(anno).name();
+ desc = MBeanOperationParameter.class.cast(anno).description();
+ paramInfo = new MBeanParameterInfo(name, type, desc);
+ }
+ }
+
+
+ if (paramInfo == null)
+ {
+ paramInfo = new MBeanParameterInfo("p " + (i + 1), type, "parameter " + (i + 1));
+ }
+ if (paramInfo != null)
+ {
+ paramsInfo[i] = paramInfo;
+ }
+ }
+
+ return paramsInfo;
+ }
+
+ /**
+ * Introspects the MBean class for constructors
+ * @param implClass
+ * @return MBeanConstructorInfo[]
+ */
+ static MBeanConstructorInfo[] getMBeanConstructorsInfo(Class implClass)
+ {
+ List<MBeanConstructorInfo> constructors = new ArrayList<MBeanConstructorInfo>();
+
+ for (Constructor cons : implClass.getConstructors())
+ {
+ MBeanConstructorInfo constructorInfo = getMBeanConstructorInfo(cons);
+ if (constructorInfo != null)
+ {
+ constructors.add(constructorInfo);
+ }
+ }
+
+ return constructors.toArray(new MBeanConstructorInfo[0]);
+ }
+
+ /**
+ * Retrieves the constructor info from given constructor.
+ * @param cons
+ * @return MBeanConstructorInfo
+ */
+ private static MBeanConstructorInfo getMBeanConstructorInfo(Constructor cons)
+ {
+ String desc = _defaultConstructorDescription;
+ Annotation anno = cons.getAnnotation(MBeanConstructor.class);
+ if (anno != null && MBeanConstructor.class.isInstance(anno))
+ {
+ desc = MBeanConstructor.class.cast(anno).value();
+ if(desc == null)
+ {
+ desc = _defaultConstructorDescription;
+ }
+ }
+
+ return new MBeanConstructorInfo(cons.getName(), desc, null);
+ }
+
+ /**
+ * Retrieves the description from the annotations of given class
+ * @param annotatedClass
+ * @return class description
+ */
+ static String getMBeanDescription(Class annotatedClass)
+ {
+ Annotation anno = annotatedClass.getAnnotation(MBeanDescription.class);
+ if (anno != null && MBeanDescription.class.isInstance(anno))
+ {
+ return MBeanDescription.class.cast(anno).value();
+ }
+ return _defaultMbeanDescription;
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java
new file mode 100644
index 0000000000..49f06d5121
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java
@@ -0,0 +1,382 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
+import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.security.SecurityManager;
+import org.apache.qpid.server.security.access.Operation;
+
+import javax.management.Attribute;
+import javax.management.JMException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXPrincipal;
+import javax.management.remote.MBeanServerForwarder;
+import javax.security.auth.Subject;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. It delegates
+ * JMX access decisions to the SecurityPlugin.
+ */
+public class MBeanInvocationHandlerImpl implements InvocationHandler, NotificationListener
+{
+ private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class);
+
+ private final IApplicationRegistry _appRegistry = ApplicationRegistry.getInstance();
+ private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate";
+ private MBeanServer _mbs;
+ private final ManagementActor _logActor = new ManagementActor(_appRegistry.getRootMessageLogger());
+ private final boolean _managementRightsInferAllAccess =
+ _appRegistry.getConfiguration().getManagementRightsInferAllAccess();
+
+ public static MBeanServerForwarder newProxyInstance()
+ {
+ final InvocationHandler handler = new MBeanInvocationHandlerImpl();
+ final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class };
+
+ Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler);
+ return MBeanServerForwarder.class.cast(proxy);
+ }
+
+ private boolean invokeDirectly(String methodName, Object[] args, Subject subject)
+ {
+ // Allow operations performed locally on behalf of the connector server itself
+ if (subject == null)
+ {
+ return true;
+ }
+
+ if (args == null || DELEGATE.equals(args[0]))
+ {
+ return true;
+ }
+
+ // Allow querying available object names and mbeans
+ if (methodName.equals("queryNames") || methodName.equals("queryMBeans"))
+ {
+ return true;
+ }
+
+ if (args[0] instanceof ObjectName)
+ {
+ ObjectName mbean = (ObjectName) args[0];
+
+ if(!DefaultManagedObject.DOMAIN.equalsIgnoreCase(mbean.getDomain()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ String methodName = method.getName();
+
+ if (methodName.equals("getMBeanServer"))
+ {
+ return _mbs;
+ }
+
+ if (methodName.equals("setMBeanServer"))
+ {
+ if (args[0] == null)
+ {
+ throw new IllegalArgumentException("Null MBeanServer");
+ }
+ if (_mbs != null)
+ {
+ throw new IllegalArgumentException("MBeanServer object already initialized");
+ }
+ _mbs = (MBeanServer) args[0];
+ return null;
+ }
+
+ // Restrict access to "createMBean" and "unregisterMBean" to any user
+ if (methodName.equals("createMBean") || methodName.equals("unregisterMBean"))
+ {
+ _logger.debug("User trying to create or unregister an MBean");
+ throw new SecurityException("Access denied: " + methodName);
+ }
+
+ // Retrieve Subject from current AccessControlContext
+ AccessControlContext acc = AccessController.getContext();
+ Subject subject = Subject.getSubject(acc);
+
+ try
+ {
+ if(invokeDirectly(methodName, args, subject))
+ {
+ return method.invoke(_mbs, args);
+ }
+
+ // Retrieve JMXPrincipal from Subject
+ Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
+ if (principals == null || principals.isEmpty())
+ {
+ throw new SecurityException("Access denied: no JMX principal");
+ }
+
+ // Save the subject
+ SecurityManager.setThreadSubject(subject);
+
+ // Get the component, type and impact, which may be null
+ String type = getType(method, args);
+ String vhost = getVirtualHost(method, args);
+ int impact = getImpact(method, args);
+
+ // Get the security manager for the virtual host (if set)
+ SecurityManager security;
+ if (vhost == null)
+ {
+ security = _appRegistry.getSecurityManager();
+ }
+ else
+ {
+ security = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager();
+ }
+
+ methodName = getMethodName(method, args);
+ if (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO)
+ {
+ // Check for read-only method invocation permission
+ if (!security.authoriseMethod(Operation.ACCESS, type, methodName))
+ {
+ throw new SecurityException("Permission denied: Access " + methodName);
+ }
+ }
+ else
+ {
+ // Check for setting properties permission
+ if (!security.authoriseMethod(Operation.UPDATE, type, methodName))
+ {
+ throw new SecurityException("Permission denied: Update " + methodName);
+ }
+ }
+
+ boolean oldAccessChecksDisabled = false;
+ if(_managementRightsInferAllAccess)
+ {
+ oldAccessChecksDisabled = SecurityManager.setAccessChecksDisabled(true);
+ }
+
+ try
+ {
+ return doInvokeWrappingWithManagementActor(method, args);
+ }
+ finally
+ {
+ if(_managementRightsInferAllAccess)
+ {
+ SecurityManager.setAccessChecksDisabled(oldAccessChecksDisabled);
+ }
+ }
+ }
+ catch (InvocationTargetException e)
+ {
+ throw e.getTargetException();
+ }
+ }
+
+ private Object doInvokeWrappingWithManagementActor(Method method,
+ Object[] args) throws IllegalAccessException,
+ InvocationTargetException
+ {
+ try
+ {
+ CurrentActor.set(_logActor);
+ return method.invoke(_mbs, args);
+ }
+ finally
+ {
+ CurrentActor.remove();
+ }
+ }
+
+ private String getType(Method method, Object[] args)
+ {
+ if (args[0] instanceof ObjectName)
+ {
+ ObjectName object = (ObjectName) args[0];
+ String type = object.getKeyProperty("type");
+
+ return type;
+ }
+ return null;
+ }
+
+ private String getVirtualHost(Method method, Object[] args)
+ {
+ if (args[0] instanceof ObjectName)
+ {
+ ObjectName object = (ObjectName) args[0];
+ String vhost = object.getKeyProperty("VirtualHost");
+
+ if(vhost != null)
+ {
+ try
+ {
+ //if the name is quoted in the ObjectName, unquote it
+ vhost = ObjectName.unquote(vhost);
+ }
+ catch(IllegalArgumentException e)
+ {
+ //ignore, this just means the name is not quoted
+ //and can be left unchanged
+ }
+ }
+
+ return vhost;
+ }
+ return null;
+ }
+
+ private String getMethodName(Method method, Object[] args)
+ {
+ String methodName = method.getName();
+
+ // if arguments are set, try and work out real method name
+ if (args != null && args.length >= 1 && args[0] instanceof ObjectName)
+ {
+ if (methodName.equals("getAttribute"))
+ {
+ methodName = "get" + (String) args[1];
+ }
+ else if (methodName.equals("setAttribute"))
+ {
+ methodName = "set" + ((Attribute) args[1]).getName();
+ }
+ else if (methodName.equals("invoke"))
+ {
+ methodName = (String) args[1];
+ }
+ }
+
+ return methodName;
+ }
+
+ private int getImpact(Method method, Object[] args)
+ {
+ //handle invocation of other methods on mbeans
+ if ((args[0] instanceof ObjectName) && (method.getName().equals("invoke")))
+ {
+ //get invoked method name
+ String mbeanMethod = (args.length > 1) ? (String) args[1] : null;
+ if (mbeanMethod == null)
+ {
+ return -1;
+ }
+
+ try
+ {
+ //Get the impact attribute
+ MBeanInfo mbeanInfo = _mbs.getMBeanInfo((ObjectName) args[0]);
+ if (mbeanInfo != null)
+ {
+ MBeanOperationInfo[] opInfos = mbeanInfo.getOperations();
+ for (MBeanOperationInfo opInfo : opInfos)
+ {
+ if (opInfo.getName().equals(mbeanMethod))
+ {
+ return opInfo.getImpact();
+ }
+ }
+ }
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Unable to determine mbean impact for method : " + mbeanMethod, ex);
+ }
+ }
+
+ return -1;
+ }
+
+ private boolean isAccessMethod(String methodName)
+ {
+ //handle standard get/query/is methods from MBeanServer
+ return (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("is"));
+ }
+
+ /**
+ * Receives notifications from the MBeanServer.
+ */
+ public void handleNotification(final Notification notification, final Object handback)
+ {
+ assert notification instanceof JMXConnectionNotification;
+
+ final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
+ final String type = notification.getType();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Notification connectionId : " + connectionId + " type : " + type
+ + " Notification handback : " + handback);
+ }
+
+ // Normally JMXManagedObjectRegistry provides a Map as handback data containing a map
+ // between connection id and username.
+ String user = null;
+ if (handback instanceof Map)
+ {
+ final Map<String, String> connectionIdUsernameMap = (Map<String, String>) handback;
+ user = connectionIdUsernameMap.get(connectionId);
+ }
+
+ // If user is still null, fallback to an unordered list of Principals from the connection id.
+ if (user == null)
+ {
+ final String[] splitConnectionId = connectionId.split(" ");
+ user = splitConnectionId[1];
+ }
+
+ if (JMXConnectionNotification.OPENED.equals(type))
+ {
+ _logActor.message(ManagementConsoleMessages.OPEN(user));
+ }
+ else if (JMXConnectionNotification.CLOSED.equals(type) ||
+ JMXConnectionNotification.FAILED.equals(type))
+ {
+ _logActor.message(ManagementConsoleMessages.CLOSE(user));
+ }
+ }
+}
+
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java
new file mode 100644
index 0000000000..83909dbe72
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.jmx;
+
+import java.util.ServiceLoader;
+
+import javax.management.JMException;
+import javax.management.StandardMBean;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+/**
+ * A provider of an mbean implementation.
+ *
+ * Provider implementations are advertised as services and loaded via {@link ServiceLoader}.
+ */
+public interface MBeanProvider
+{
+ /**
+ * Tests whether a <code>child</code> can be managed by the mbean
+ * provided by this provider.
+ */
+ boolean isChildManageableByMBean(ConfiguredObject child);
+
+ /**
+ * Creates a mbean for this child. This method should only be called if
+ * {@link #isChildManageableByMBean(ConfiguredObject)} has previously returned true.
+ *
+ * @return newly created mbean
+ */
+ StandardMBean createMBean(ConfiguredObject child, StandardMBean parent) throws JMException;
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java
new file mode 100644
index 0000000000..40b778fd93
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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.jmx;
+
+import javax.management.JMException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+/**
+ * This should be implemented by all Managable objects.
+ */
+public interface ManagedObject
+{
+ static final String DOMAIN = "org.apache.qpid";
+
+ /**
+ * @return the name that uniquely identifies this object instance. It must be
+ * unique only among objects of this type at this level in the hierarchy so
+ * the uniqueness should not be too difficult to ensure.
+ */
+ String getObjectInstanceName();
+
+ String getType();
+
+ Class<?> getManagementInterface();
+
+ ManagedObject getParentObject();
+
+ void register() throws JMException;
+
+ void unregister() throws JMException;
+
+ /**
+ * Returns the ObjectName required for the mbeanserver registration.
+ * @return ObjectName
+ * @throws javax.management.MalformedObjectNameException
+ */
+ ObjectName getObjectName() throws MalformedObjectNameException;
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java
new file mode 100644
index 0000000000..2ae0ac7052
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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.jmx;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.common.Closeable;
+
+import javax.management.JMException;
+import java.io.IOException;
+
+/**
+ * Handles the registration (and unregistration and so on) of managed objects.
+ *
+ * Managed objects are responsible for exposting attributes, operations and notifications. They will expose
+ * these outside the JVM therefore it is important not to use implementation objects directly as managed objects.
+ * Instead, creating inner classes and exposing those is an effective way of exposing internal state in a
+ * controlled way.
+ *
+ * Although we do not explictly use them while targetting Java 5, the enhanced MXBean approach in Java 6 will
+ * be the obvious choice for managed objects.
+ *
+ */
+public interface ManagedObjectRegistry extends Closeable
+{
+ void start() throws IOException, ConfigurationException;
+
+ void registerObject(ManagedObject managedObject) throws JMException;
+
+ void unregisterObject(ManagedObject managedObject) throws JMException;
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java
new file mode 100644
index 0000000000..6ab7db3629
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java
@@ -0,0 +1,200 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import javax.management.NotCompliantMBeanException;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.VirtualHost;
+
+abstract class AbstractStatisticsGatheringMBean<T extends ConfiguredObject> extends AMQManagedObject
+{
+ private long _lastStatUpdateTime;
+ private long _statUpdatePeriod = 5000L;
+ private long _lastMessagesReceived;
+ private long _lastMessagesSent;
+ private long _lastBytesReceived;
+ private long _lastBytesSent;
+ private double _messageReceivedRate;
+ private double _messageSentRate;
+ private double _bytesReceivedRate;
+ private double _bytesSentRate;
+ private double _peakMessageReceivedRate;
+ private double _peakMessageSentRate;
+ private double _peakBytesReceivedRate;
+ private double _peakBytesSentRate;
+ private final T _configuredObject;
+
+ protected AbstractStatisticsGatheringMBean(Class<?> managementInterface,
+ String typeName,
+ ManagedObjectRegistry registry,
+ T object) throws NotCompliantMBeanException
+ {
+ super(managementInterface, typeName, registry);
+ _configuredObject = object;
+ initStats();
+ }
+
+ protected void initStats()
+ {
+ _lastStatUpdateTime = System.currentTimeMillis();
+ }
+
+ protected synchronized void updateStats()
+ {
+ long time = System.currentTimeMillis();
+ final long period = time - _lastStatUpdateTime;
+ if(period > _statUpdatePeriod)
+ {
+ long messagesReceived = getStatistic(VirtualHost.MESSAGES_IN);
+ long messagesSent = getStatistic(VirtualHost.MESSAGES_OUT);
+ long bytesReceived = getStatistic(VirtualHost.BYTES_IN);
+ long bytesSent = getStatistic(VirtualHost.BYTES_OUT);
+
+ double messageReceivedRate = (double)(messagesReceived - _lastMessagesReceived) / (double)period;
+ double messageSentRate = (double)(messagesSent - _lastMessagesSent) / (double)period;
+ double bytesReceivedRate = (double)(bytesReceived - _lastBytesReceived) / (double)period;
+ double bytesSentRate = (double)(bytesSent - _lastBytesSent) / (double)period;
+
+ _lastMessagesReceived = messagesReceived;
+ _lastMessagesSent = messagesSent;
+ _lastBytesReceived = bytesReceived;
+ _lastBytesSent = bytesSent;
+
+ _messageReceivedRate = messageReceivedRate;
+ _messageSentRate = messageSentRate;
+ _bytesReceivedRate = bytesReceivedRate;
+ _bytesSentRate = bytesSentRate;
+
+ if(messageReceivedRate > _peakMessageReceivedRate)
+ {
+ _peakMessageReceivedRate = messageReceivedRate;
+ }
+
+ if(messageSentRate > _peakMessageSentRate)
+ {
+ _peakMessageSentRate = messageSentRate;
+ }
+
+ if(bytesReceivedRate > _peakBytesReceivedRate)
+ {
+ _peakBytesReceivedRate = bytesReceivedRate;
+ }
+
+ if(bytesSentRate > _peakBytesSentRate)
+ {
+ _peakBytesSentRate = bytesSentRate;
+ }
+
+ }
+ }
+
+ private long getStatistic(String name)
+ {
+ return (Long) getConfiguredObject().getStatistics().getStatistic(name);
+ }
+
+ public synchronized void resetStatistics() throws Exception
+ {
+ updateStats();
+ //TODO - implement resetStatistics()
+ }
+
+ public synchronized double getPeakMessageDeliveryRate()
+ {
+ updateStats();
+ return _peakMessageSentRate;
+ }
+
+ public synchronized double getPeakDataDeliveryRate()
+ {
+ updateStats();
+ return _peakBytesSentRate;
+ }
+
+ public synchronized double getMessageDeliveryRate()
+ {
+ updateStats();
+ return _messageSentRate;
+ }
+
+ public synchronized double getDataDeliveryRate()
+ {
+ updateStats();
+ return _bytesSentRate;
+ }
+
+ public synchronized long getTotalMessagesDelivered()
+ {
+ updateStats();
+ return getStatistic(Connection.MESSAGES_OUT);
+ }
+
+ public synchronized long getTotalDataDelivered()
+ {
+ updateStats();
+ return getStatistic(Connection.BYTES_OUT);
+ }
+
+ protected final T getConfiguredObject()
+ {
+ return _configuredObject;
+ }
+
+ public synchronized double getPeakMessageReceiptRate()
+ {
+ updateStats();
+ return _peakMessageReceivedRate;
+ }
+
+ public synchronized double getPeakDataReceiptRate()
+ {
+ updateStats();
+ return _peakBytesReceivedRate;
+ }
+
+ public synchronized double getMessageReceiptRate()
+ {
+ updateStats();
+ return _messageReceivedRate;
+ }
+
+ public synchronized double getDataReceiptRate()
+ {
+ updateStats();
+ return _bytesReceivedRate;
+ }
+
+ public synchronized long getTotalMessagesReceived()
+ {
+ updateStats();
+ return getStatistic(Connection.MESSAGES_IN);
+ }
+
+ public synchronized long getTotalDataReceived()
+ {
+ updateStats();
+ return getStatistic(Connection.BYTES_IN);
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java
new file mode 100644
index 0000000000..beffb4eaa9
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import org.apache.qpid.management.common.mbeans.ConfigurationManagement;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import javax.management.JMException;
+import javax.management.NotCompliantMBeanException;
+
+public class ConfigurationManagementMBean extends AMQManagedObject implements ConfigurationManagement
+{
+
+ public ConfigurationManagementMBean(ManagedObjectRegistry registry) throws JMException
+ {
+ super(ConfigurationManagement.class, ConfigurationManagement.TYPE, registry);
+ register();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return ConfigurationManagement.TYPE;
+ }
+
+ public void reloadSecurityConfiguration() throws Exception
+ {
+ ApplicationRegistry.getInstance().getConfiguration().reparseConfigFileSecuritySections();
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java
new file mode 100644
index 0000000000..d0c0d5e73f
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java
@@ -0,0 +1,182 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import javax.management.JMException;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Session;
+import org.apache.qpid.server.model.Statistics;
+
+public class ConnectionMBean extends AbstractStatisticsGatheringMBean<Connection> implements ManagedConnection
+{
+ private static final OpenType[] CHANNEL_ATTRIBUTE_TYPES =
+ { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN };
+ private static final CompositeType CHANNEL_TYPE;
+ private static final TabularType CHANNELS_TYPE;
+
+ static
+ {
+ try
+ {
+ CHANNEL_TYPE = new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]),
+ COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]),
+ CHANNEL_ATTRIBUTE_TYPES);
+ CHANNELS_TYPE = new TabularType("Channels", "Channels", CHANNEL_TYPE, (String[]) TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()]));
+ }
+ catch (JMException ex)
+ {
+ // This is not expected to ever occur.
+ throw new RuntimeException("Got JMException in static initializer.", ex);
+ }
+ }
+
+
+ private final VirtualHostMBean _virtualHostMBean;
+
+ public ConnectionMBean(Connection conn, VirtualHostMBean virtualHostMBean) throws JMException
+ {
+ super(ManagedConnection.class, ManagedConnection.TYPE, virtualHostMBean.getRegistry(), conn);
+ _virtualHostMBean = virtualHostMBean;
+ register();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return ObjectName.quote(getRemoteAddress());
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return _virtualHostMBean;
+ }
+
+ public String getClientId()
+ {
+ return (String) getConfiguredObject().getAttribute(Connection.CLIENT_ID);
+ }
+
+ public String getAuthorizedId()
+ {
+ return (String) getConfiguredObject().getAttribute(Connection.PRINCIPAL);
+ }
+
+ public String getVersion()
+ {
+ return (String) getConfiguredObject().getAttribute(Connection.CLIENT_VERSION);
+ }
+
+ public String getRemoteAddress()
+ {
+ return (String) getConfiguredObject().getAttribute(Connection.REMOTE_ADDRESS);
+ }
+
+ public Date getLastIoTime()
+ {
+ Long lastIo = (Long) getConfiguredObject().getStatistics().getStatistic(Connection.LAST_IO_TIME);
+ return new Date(lastIo);
+ }
+
+ public Long getMaximumNumberOfChannels()
+ {
+ return (Long) getConfiguredObject().getAttribute(Connection.SESSION_COUNT_LIMIT);
+ }
+
+ public TabularData channels() throws IOException, JMException
+ {
+ TabularDataSupport sessionTable = new TabularDataSupport(CHANNELS_TYPE);
+ Collection<Session> list = getConfiguredObject().getSessions();
+
+ for (Session session : list)
+ {
+ Statistics statistics = session.getStatistics();
+ Long txnBegins = (Long) statistics.getStatistic(Session.LOCAL_TRANSACTION_BEGINS);
+ Integer channelId = (Integer) session.getAttribute(Session.CHANNEL_ID);
+ int unacknowledgedSize = ((Number) statistics.getStatistic(Session.UNACKNOWLEDGED_MESSAGES)).intValue();
+ boolean blocked = (Boolean) session.getAttribute(Session.PRODUCER_FLOW_BLOCKED);
+ boolean isTransactional = (txnBegins>0l);
+
+ Object[] itemValues =
+ {
+ channelId,
+ isTransactional,
+ null, // TODO - default queue (which is meaningless)
+ unacknowledgedSize,
+ blocked
+ };
+
+ CompositeData sessionData = new CompositeDataSupport(CHANNEL_TYPE,
+ COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues);
+ sessionTable.put(sessionData);
+ }
+
+ return sessionTable;
+ }
+
+ public void commitTransactions(int channelId) throws JMException
+ {
+ throw buildUnsupportedException();
+ }
+
+ public void rollbackTransactions(int channelId) throws JMException
+ {
+ throw buildUnsupportedException();
+ }
+
+ public void closeConnection() throws Exception
+ {
+ getConfiguredObject().delete();
+ }
+
+ public boolean isStatisticsEnabled()
+ {
+ return true;
+ }
+
+ public void setStatisticsEnabled(boolean enabled)
+ {
+ updateStats();
+ }
+
+ private JMException buildUnsupportedException() throws JMException
+ {
+ String msg = "Operation not supported";
+ JMException jmException = new JMException(msg);
+ jmException.initCause(new UnsupportedOperationException(msg));
+ return jmException;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java
new file mode 100644
index 0000000000..56802d0403
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java
@@ -0,0 +1,313 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.model.Binding;
+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.VirtualHost;
+
+import javax.management.JMException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExchangeMBean extends AMQManagedObject implements ManagedExchange
+{
+
+ public static final String FANOUT_EXCHANGE_TYPE = "fanout";
+ public static final String HEADERS_EXCHANGE_TYPE = "headers";
+
+ private static final String[] TABULAR_UNIQUE_INDEX_ARRAY =
+ TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()]);
+
+ private static final String[] COMPOSITE_ITEM_NAMES_ARRAY =
+ COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]);
+
+ private static final String[] COMPOSITE_ITEM_DESCRIPTIONS_ARRAY =
+ COMPOSITE_ITEM_DESCRIPTIONS.toArray(new String[COMPOSITE_ITEM_DESCRIPTIONS.size()]);
+
+ private static final OpenType[] BINDING_ITEM_TYPES;
+ private static final CompositeType BINDING_DATA_TYPE;
+ private static final OpenType[] HEADERS_BINDING_ITEM_TYPES;
+
+
+ private static final CompositeType HEADERS_BINDING_DATA_TYPE;
+
+ private static final String[] HEADERS_COMPOSITE_ITEM_NAMES_ARRAY =
+ HEADERS_COMPOSITE_ITEM_NAMES.toArray(new String[HEADERS_COMPOSITE_ITEM_NAMES.size()]);
+
+ private static final String[] HEADERS_COMPOSITE_ITEM_DESCS_ARRAY =
+ HEADERS_COMPOSITE_ITEM_DESC.toArray(new String[HEADERS_COMPOSITE_ITEM_DESC.size()]);
+ private static final String[] HEADERS_TABULAR_UNIQUE_INDEX_ARRAY =
+ HEADERS_TABULAR_UNIQUE_INDEX.toArray(new String[HEADERS_TABULAR_UNIQUE_INDEX.size()]);
+
+ static
+ {
+ try
+ {
+ BINDING_ITEM_TYPES = new OpenType[] {SimpleType.STRING, new ArrayType(1, SimpleType.STRING)};
+
+ BINDING_DATA_TYPE= new CompositeType("Exchange Binding", "Binding key and Queue names",
+ COMPOSITE_ITEM_NAMES_ARRAY,
+ COMPOSITE_ITEM_DESCRIPTIONS_ARRAY,
+ BINDING_ITEM_TYPES);
+
+ HEADERS_BINDING_ITEM_TYPES = new OpenType[] {SimpleType.INTEGER,
+ SimpleType.STRING,
+ new ArrayType(1, SimpleType.STRING)};
+
+ HEADERS_BINDING_DATA_TYPE = new CompositeType("Exchange Binding", "Queue name and header bindings",
+ HEADERS_COMPOSITE_ITEM_NAMES_ARRAY,
+ HEADERS_COMPOSITE_ITEM_DESCS_ARRAY,
+ HEADERS_BINDING_ITEM_TYPES);
+
+
+ }
+ catch(OpenDataException e)
+ {
+ throw new RuntimeException("Unexpected Error creating ArrayType", e);
+ }
+ }
+
+
+ private final Exchange _exchange;
+ private final VirtualHostMBean _vhostMBean;
+
+ protected ExchangeMBean(Exchange exchange, VirtualHostMBean virtualHostMBean)
+ throws JMException
+ {
+ super(ManagedExchange.class, ManagedExchange.TYPE, virtualHostMBean.getRegistry());
+ _exchange = exchange;
+ _vhostMBean = virtualHostMBean;
+
+ register();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return ObjectName.quote(getName());
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return _vhostMBean;
+ }
+
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ String objNameString = super.getObjectName().toString();
+ objNameString = objNameString + ",ExchangeType=" + getExchangeType();
+ return new ObjectName(objNameString);
+ }
+
+
+ public String getName()
+ {
+ return _exchange.getName();
+ }
+
+ public String getExchangeType()
+ {
+ return _exchange.getExchangeType();
+ }
+
+ public Integer getTicketNo()
+ {
+ return 0;
+ }
+
+ public boolean isDurable()
+ {
+ return _exchange.isDurable();
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _exchange.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE;
+ }
+
+ public TabularData bindings() throws IOException, JMException
+ {
+ if(HEADERS_EXCHANGE_TYPE.equals(_exchange.getExchangeType()))
+ {
+ return getHeadersBindings(_exchange.getBindings());
+ }
+ else
+ {
+ return getNonHeadersBindings(_exchange.getBindings());
+ }
+ }
+
+ private TabularData getHeadersBindings(Collection<Binding> bindings) throws OpenDataException
+ {
+ TabularType bindinglistDataType =
+ new TabularType("Exchange Bindings", "List of exchange bindings for " + getName(),
+ HEADERS_BINDING_DATA_TYPE,
+ HEADERS_TABULAR_UNIQUE_INDEX_ARRAY);
+
+ TabularDataSupport bindingList = new TabularDataSupport(bindinglistDataType);
+ int count = 1;
+ for (Binding binding : bindings)
+ {
+
+ String queueName = binding.getParent(Queue.class).getName();
+
+
+ Map<String,Object> headerMappings = binding.getArguments();
+
+ final List<String> mappingList = new ArrayList<String>();
+
+ if(headerMappings != null)
+ {
+ for(Map.Entry<String,Object> entry : headerMappings.entrySet())
+ {
+
+ mappingList.add(entry.getKey() + "=" + entry.getValue());
+ }
+ }
+
+
+ Object[] bindingItemValues = {count++, queueName, mappingList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(HEADERS_BINDING_DATA_TYPE,
+ HEADERS_COMPOSITE_ITEM_NAMES_ARRAY,
+ bindingItemValues);
+ bindingList.put(bindingData);
+ }
+
+ return bindingList;
+
+ }
+
+ private TabularData getNonHeadersBindings(Collection<Binding> bindings) throws OpenDataException
+ {
+
+ TabularType bindinglistDataType =
+ new TabularType("Exchange Bindings", "Exchange Bindings for " + getName(),
+ BINDING_DATA_TYPE,
+ TABULAR_UNIQUE_INDEX_ARRAY);
+
+ TabularDataSupport bindingList = new TabularDataSupport(bindinglistDataType);
+
+ Map<String, List<String>> bindingMap = new HashMap<String, List<String>>();
+
+ for (Binding binding : bindings)
+ {
+ String key = FANOUT_EXCHANGE_TYPE.equals(_exchange.getExchangeType()) ? "*" : binding.getName();
+ List<String> queueList = bindingMap.get(key);
+ if(queueList == null)
+ {
+ queueList = new ArrayList<String>();
+ bindingMap.put(key, queueList);
+ }
+ queueList.add(binding.getParent(Queue.class).getName());
+
+ }
+
+ for(Map.Entry<String, List<String>> entry : bindingMap.entrySet())
+ {
+ Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(BINDING_DATA_TYPE,
+ COMPOSITE_ITEM_NAMES_ARRAY,
+ bindingItemValues);
+ bindingList.put(bindingData);
+ }
+
+ return bindingList;
+ }
+
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ final Map<String,Object> arguments = new HashMap<String, Object>();
+
+ if(HEADERS_EXCHANGE_TYPE.equals(_exchange.getExchangeType()))
+ {
+ final String[] bindings = binding.split(",");
+ for (int i = 0; i < bindings.length; i++)
+ {
+ final String[] keyAndValue = bindings[i].split("=");
+ if (keyAndValue == null || keyAndValue.length == 0 || keyAndValue.length > 2 || keyAndValue[0].length() == 0)
+ {
+ throw new JMException("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\"");
+ }
+
+ if(keyAndValue.length == 1)
+ {
+ //no value was given, only a key. Use an empty value to signal match on key presence alone
+ arguments.put(keyAndValue[0], "");
+ }
+ else
+ {
+ arguments.put(keyAndValue[0], keyAndValue[1]);
+ }
+ }
+ }
+
+ VirtualHost virtualHost = _exchange.getParent(VirtualHost.class);
+ Queue queue = MBeanUtils.findQueueFromQueueName(virtualHost, queueName);
+ _exchange.createBinding(binding, queue, arguments, Collections.EMPTY_MAP);
+ }
+
+ public void removeBinding(String queueName, String bindingKey)
+ throws IOException, JMException
+ {
+ VirtualHost virtualHost = _exchange.getParent(VirtualHost.class);
+ Queue queue = MBeanUtils.findQueueFromQueueName(virtualHost, queueName);;
+
+ boolean deleted = false;
+ for(Binding binding : _exchange.getBindings())
+ {
+ if(queue.equals(binding.getParent(Queue.class)) && bindingKey.equals(binding.getName()))
+ {
+ binding.delete();
+ deleted = true;
+ }
+ }
+
+ if (!deleted)
+ {
+ throw new OperationsException("No such binding \"" + bindingKey + "\" on queue \"" + queueName + "\"");
+ }
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java
new file mode 100644
index 0000000000..0dac8ebe37
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java
@@ -0,0 +1,324 @@
+/*
+ * 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.jmx.mbeans;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
+import org.apache.qpid.server.logging.log4j.LoggingFacadeException;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+/** MBean class for LoggingManagement. It implements all the management features exposed for managing logging. */
+@MBeanDescription("Logging Management Interface")
+public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement
+{
+ public static final String INHERITED_PSUEDO_LOG_LEVEL = "INHERITED";
+ private static final Logger LOGGER = Logger.getLogger(LoggingManagementMBean.class);
+ private static final TabularType LOGGER_LEVEL_TABULAR_TYE;
+ private static final CompositeType LOGGER_LEVEL_COMPOSITE_TYPE;
+
+ private final LoggingFacade _configurator;
+
+ static
+ {
+ try
+ {
+ OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING};
+
+ LOGGER_LEVEL_COMPOSITE_TYPE = new CompositeType("LoggerLevelList", "Logger Level Data",
+ COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]),
+ COMPOSITE_ITEM_DESCRIPTIONS.toArray(new String[COMPOSITE_ITEM_DESCRIPTIONS.size()]),
+ loggerLevelItemTypes);
+
+ LOGGER_LEVEL_TABULAR_TYE = new TabularType("LoggerLevel", "List of loggers with levels",
+ LOGGER_LEVEL_COMPOSITE_TYPE,
+ TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()]));
+ }
+ catch (OpenDataException e)
+ {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ public LoggingManagementMBean(LoggingFacade configurator, ManagedObjectRegistry registry) throws JMException
+ {
+ super(LoggingManagement.class, LoggingManagement.TYPE, registry);
+ register();
+ _configurator = configurator;
+ }
+
+ @Override
+ public String getObjectInstanceName()
+ {
+ return LoggingManagement.TYPE;
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+
+ @Override
+ public Integer getLog4jLogWatchInterval()
+ {
+ return _configurator.getLog4jLogWatchInterval();
+ }
+
+ @Override
+ public String[] getAvailableLoggerLevels()
+ {
+ List<String> levels = _configurator.getAvailableLoggerLevels();
+ List<String> mbeanLevels = new ArrayList<String>(levels);
+ mbeanLevels.add(INHERITED_PSUEDO_LOG_LEVEL);
+
+ return mbeanLevels.toArray(new String[mbeanLevels.size()]);
+ }
+
+ @Override
+ public TabularData viewEffectiveRuntimeLoggerLevels()
+ {
+ Map<String, String> levels = _configurator.retrieveRuntimeLoggersLevels();
+ return createTabularDataFromLevelsMap(levels);
+ }
+
+ @Override
+ public String getRuntimeRootLoggerLevel()
+ {
+ return _configurator.retrieveRuntimeRootLoggerLevel();
+ }
+
+ @Override
+ public boolean setRuntimeRootLoggerLevel(String level)
+ {
+ try
+ {
+ validateLevelNotAllowingInherited(level);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ LOGGER.warn(level + " is not a known level");
+ return false;
+ }
+
+ _configurator.setRuntimeRootLoggerLevel(level);
+ return true;
+ }
+
+ @Override
+ public boolean setRuntimeLoggerLevel(String logger, String level)
+ {
+ String validatedLevel;
+ try
+ {
+ validatedLevel = getValidateLevelAllowingInherited(level);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ LOGGER.warn(level + " is not a known level");
+ return false;
+ }
+
+ try
+ {
+ _configurator.setRuntimeLoggerLevel(logger, validatedLevel);
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.error("Cannot set runtime logging level", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public TabularData viewConfigFileLoggerLevels()
+ {
+ Map<String,String> levels;
+ try
+ {
+ levels = _configurator.retrieveConfigFileLoggersLevels();
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.error("Cannot determine logging levels", e);
+ return null;
+ }
+
+ return createTabularDataFromLevelsMap(levels);
+ }
+
+ @Override
+ public String getConfigFileRootLoggerLevel()throws IOException
+ {
+ try
+ {
+ return _configurator.retrieveConfigFileRootLoggerLevel().toUpperCase();
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.warn("The log4j configuration get config request was aborted: ", e);
+ throw new IOException("The log4j configuration get config request was aborted: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean setConfigFileLoggerLevel(String logger, String level)
+ {
+ String validatedLevel;
+ try
+ {
+ validatedLevel = getValidateLevelAllowingInherited(level);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ LOGGER.warn(level + " is not a known level");
+ return false;
+ }
+
+ try
+ {
+ _configurator.setConfigFileLoggerLevel(logger, validatedLevel);
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.warn("The log4j configuration set config request was aborted: ", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean setConfigFileRootLoggerLevel(String level)
+ {
+ try
+ {
+ validateLevelNotAllowingInherited(level);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ LOGGER.warn(level + " is not a known level");
+ return false;
+ }
+
+ try
+ {
+ _configurator.setConfigFileRootLoggerLevel(level);
+ return true;
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.warn("The log4j configuration set config request was aborted: ", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void reloadConfigFile() throws IOException
+ {
+ try
+ {
+
+ _configurator.reload();
+ }
+ catch (LoggingFacadeException e)
+ {
+ LOGGER.warn("The log4j configuration reload request was aborted: ", e);
+ throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
+ }
+ }
+
+ private String getValidateLevelAllowingInherited(String level)
+ {
+ if(level == null
+ || "null".equalsIgnoreCase(level)
+ || INHERITED_PSUEDO_LOG_LEVEL.equalsIgnoreCase(level))
+ {
+ //the string "null" or "inherited" signals to inherit from a parent logger,
+ //using a null Level reference for the logger.
+ return null;
+ }
+
+ validateLevelNotAllowingInherited(level);
+ return level;
+ }
+
+ private void validateLevelNotAllowingInherited(String level)
+ {
+ final List<String> availableLoggerLevels = _configurator.getAvailableLoggerLevels();
+ if (!availableLoggerLevels.contains(level)
+ && !availableLoggerLevels.contains(String.valueOf(level).toUpperCase()))
+ {
+ throw new IllegalArgumentException(level + " not known");
+ }
+ }
+
+ private TabularData createTabularDataFromLevelsMap(Map<String, String> levels)
+ {
+ TabularData loggerLevelList = new TabularDataSupport(LOGGER_LEVEL_TABULAR_TYE);
+ for (Map.Entry<String,String> entry : levels.entrySet())
+ {
+ String loggerName = entry.getKey();
+ String level = entry.getValue();
+
+ CompositeData loggerData = createRow(loggerName, level);
+ loggerLevelList.put(loggerData);
+ }
+ return loggerLevelList;
+ }
+
+
+ private CompositeData createRow(String loggerName, String level)
+ {
+ Object[] itemData = {loggerName, level.toUpperCase()};
+ try
+ {
+ CompositeData loggerData = new CompositeDataSupport(LOGGER_LEVEL_COMPOSITE_TYPE,
+ COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData);
+ return loggerData;
+ }
+ catch (OpenDataException ode)
+ {
+ // Should not happen
+ throw new RuntimeException(ode);
+ }
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java
new file mode 100644
index 0000000000..97e84d4796
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import javax.management.OperationsException;
+
+import org.apache.qpid.server.model.ConfiguredObjectFinder;
+import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+
+public class MBeanUtils
+{
+ public static Queue findQueueFromQueueName(VirtualHost virtualHost, String queueName) throws OperationsException
+ {
+ Queue queue = ConfiguredObjectFinder.findConfiguredObjectByName(virtualHost.getQueues(), queueName);
+ if (queue == null)
+ {
+ throw new OperationsException("No such queue \""+queueName+"\"");
+ }
+ else
+ {
+ return queue;
+ }
+ }
+
+ public static Exchange findExchangeFromExchangeName(VirtualHost virtualHost, String exchangeName) throws OperationsException
+ {
+ Exchange exchange = ConfiguredObjectFinder.findConfiguredObjectByName(virtualHost.getExchanges(), exchangeName);
+ if (exchange == null)
+ {
+ throw new OperationsException("No such exchange \""+exchangeName+"\"");
+ }
+ else
+ {
+ return exchange;
+ }
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java
new file mode 100644
index 0000000000..5c8b0f7194
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java
@@ -0,0 +1,668 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.management.JMException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.monitor.MonitorNotification;
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import org.apache.commons.lang.time.FastDateFormat;
+import org.apache.log4j.Logger;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.message.AMQMessageHeader;
+import org.apache.qpid.server.message.ServerMessage;
+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.QueueNotificationListener;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.NotificationCheck;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.QueueEntryVisitor;
+
+public class QueueMBean extends AMQManagedObject implements ManagedQueue, QueueNotificationListener
+{
+ private static final Logger LOGGER = Logger.getLogger(QueueMBean.class);
+
+ private static final String[] VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC_ARRAY =
+ VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]);
+
+ private static final OpenType[] MSG_ATTRIBUTE_TYPES;
+ private static final CompositeType MSG_DATA_TYPE;
+ private static final TabularType MSG_LIST_DATA_TYPE;
+ private static final CompositeType MSG_CONTENT_TYPE;
+ private static final String[] VIEW_MSG_COMPOSIT_ITEM_NAMES_ARRAY = VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.toArray(
+ new String[VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.size()]);
+
+ static
+ {
+
+ try
+ {
+ MSG_ATTRIBUTE_TYPES = new OpenType[] {
+ SimpleType.LONG, // For message id
+ new ArrayType(1, SimpleType.STRING), // For header attributes
+ SimpleType.LONG, // For size
+ SimpleType.BOOLEAN, // For redelivered
+ SimpleType.LONG, // For queue position
+ SimpleType.INTEGER // For delivery count}
+ };
+
+ MSG_DATA_TYPE = new CompositeType("Message", "AMQ Message",
+ VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC_ARRAY,
+ VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC_ARRAY, MSG_ATTRIBUTE_TYPES);
+
+ MSG_LIST_DATA_TYPE = new TabularType("Messages", "List of messages", MSG_DATA_TYPE,
+ VIEW_MSGS_TABULAR_UNIQUE_INDEX.toArray(new String[VIEW_MSGS_TABULAR_UNIQUE_INDEX.size()]));
+
+ OpenType[] msgContentAttrs = new OpenType[] {
+ SimpleType.LONG, // For message id
+ SimpleType.STRING, // For MimeType
+ SimpleType.STRING, // For MimeType
+ new ArrayType(SimpleType.BYTE, true) // For message content
+ };
+
+
+ MSG_CONTENT_TYPE = new CompositeType("Message Content", "AMQ Message Content",
+ VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.size()]),
+ VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.size()]),
+ msgContentAttrs);
+
+ }
+ catch (OpenDataException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private final Queue _queue;
+ private final VirtualHostMBean _vhostMBean;
+
+ /** Date/time format used for message expiration and message timestamp formatting */
+ public static final String JMSTIMESTAMP_DATETIME_FORMAT = "MM-dd-yy HH:mm:ss.SSS z";
+
+ private static final FastDateFormat FAST_DATE_FORMAT = FastDateFormat.getInstance(JMSTIMESTAMP_DATETIME_FORMAT);
+
+ public QueueMBean(Queue queue, VirtualHostMBean virtualHostMBean) throws JMException
+ {
+ super(ManagedQueue.class, ManagedQueue.TYPE, virtualHostMBean.getRegistry());
+ _queue = queue;
+ _vhostMBean = virtualHostMBean;
+ register();
+ _queue.setNotificationListener(this);
+ }
+
+ public ManagedObject getParentObject()
+ {
+ return _vhostMBean;
+ }
+
+ public String getObjectInstanceName()
+ {
+ return ObjectName.quote(getName());
+ }
+
+ public String getName()
+ {
+ return _queue.getName();
+ }
+
+ public Integer getMessageCount()
+ {
+ return getStatisticValue(Queue.QUEUE_DEPTH_MESSAGES).intValue();
+ }
+
+ public Integer getMaximumDeliveryCount()
+ {
+ return (Integer) _queue.getAttribute(Queue.MAXIMUM_DELIVERY_ATTEMPTS);
+ }
+
+ public Long getReceivedMessageCount()
+ {
+ return getStatisticValue(Queue.TOTAL_ENQUEUED_MESSAGES).longValue();
+ }
+
+ public Long getQueueDepth()
+ {
+ return getStatisticValue(Queue.QUEUE_DEPTH_BYTES).longValue();
+ }
+
+ public Integer getActiveConsumerCount()
+ {
+ return getStatisticValue(Queue.CONSUMER_COUNT_WITH_CREDIT).intValue();
+ }
+
+ public Integer getConsumerCount()
+ {
+ return getStatisticValue(Queue.CONSUMER_COUNT).intValue();
+ }
+
+ public String getOwner()
+ {
+ return (String) _queue.getAttribute(Queue.OWNER);
+ }
+
+ @Override
+ public String getQueueType()
+ {
+ return (String) _queue.getAttribute(Queue.TYPE);
+ }
+
+ public boolean isDurable()
+ {
+ return _queue.isDurable();
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _queue.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE;
+ }
+
+ public Long getMaximumMessageAge()
+ {
+ return (Long) _queue.getAttribute(Queue.ALERT_THRESHOLD_MESSAGE_AGE);
+ }
+
+ public void setMaximumMessageAge(Long age)
+ {
+ _queue.setAttribute(Queue.ALERT_THRESHOLD_MESSAGE_AGE, getMaximumMessageAge(), age);
+ }
+
+ public Long getMaximumMessageSize()
+ {
+ return (Long) _queue.getAttribute(Queue.ALERT_THRESHOLD_MESSAGE_SIZE);
+ }
+
+ public void setMaximumMessageSize(Long size)
+ {
+ _queue.setAttribute(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, getMaximumMessageSize(), size);
+ }
+
+ public Long getMaximumMessageCount()
+ {
+ return (Long) _queue.getAttribute(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES);
+ }
+
+ public void setMaximumMessageCount(Long value)
+ {
+ _queue.setAttribute(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, getMaximumMessageCount(), value);
+ }
+
+ public Long getMaximumQueueDepth()
+ {
+ return (Long) _queue.getAttribute(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES);
+ }
+
+ public void setMaximumQueueDepth(Long value)
+ {
+ _queue.setAttribute(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, getMaximumQueueDepth(), value);
+ }
+
+ public Long getCapacity()
+ {
+ return (Long) _queue.getAttribute(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES);
+ }
+
+ public void setCapacity(Long value)
+ {
+ _queue.setAttribute(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, getCapacity(), value);
+ }
+
+ public Long getFlowResumeCapacity()
+ {
+ return (Long) _queue.getAttribute(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES);
+ }
+
+ public void setFlowResumeCapacity(Long value)
+ {
+ _queue.setAttribute(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, getFlowResumeCapacity(), value);
+ }
+
+ public boolean isFlowOverfull()
+ {
+ return (Boolean)_queue.getAttribute(Queue.QUEUE_FLOW_STOPPED);
+ }
+
+ public boolean isExclusive()
+ {
+ return (Boolean) _queue.getAttribute(Queue.EXCLUSIVE);
+ }
+
+ public void setExclusive(boolean exclusive)
+ {
+ _queue.setAttribute(Queue.EXCLUSIVE, isExclusive(), exclusive);
+ }
+
+ public void setAlternateExchange(String exchangeName) throws OperationsException
+ {
+ if (exchangeName == null || "".equals(exchangeName))
+ {
+ _queue.setAttribute(Queue.ALTERNATE_EXCHANGE, getAlternateExchange(), null);
+ }
+ else
+ {
+ VirtualHost virtualHost = _queue.getParent(VirtualHost.class);
+ Exchange exchange = MBeanUtils.findExchangeFromExchangeName(virtualHost, exchangeName);
+
+ _queue.setAttribute(Queue.ALTERNATE_EXCHANGE, getAlternateExchange(), exchange);
+ }
+ }
+
+ public String getAlternateExchange()
+ {
+ Exchange alternateExchange = (Exchange) _queue.getAttribute(Queue.ALTERNATE_EXCHANGE);
+ return alternateExchange == null ? null : alternateExchange.getName();
+ }
+
+ public TabularData viewMessages(int fromIndex, int toIndex)
+ throws IOException, JMException
+ {
+ return viewMessages((long)fromIndex, (long)toIndex);
+ }
+
+ public TabularData viewMessages(long startPosition, long endPosition)
+ throws IOException, JMException
+ {
+ if ((startPosition > endPosition) || (startPosition < 1))
+ {
+ throw new OperationsException("From Index = " + startPosition + ", To Index = " + endPosition
+ + "\n\"From Index\" should be greater than 0 and less than \"To Index\"");
+ }
+
+ if ((endPosition - startPosition) > Integer.MAX_VALUE)
+ {
+ throw new OperationsException("Specified MessageID interval is too large. Intervals must be less than 2^31 in size");
+ }
+
+
+ List<QueueEntry> messages = getMessages(startPosition, endPosition);
+
+ TabularDataSupport messageTable = new TabularDataSupport(MSG_LIST_DATA_TYPE);
+
+
+ // Create the tabular list of message header contents
+ long position = startPosition;
+
+ for (QueueEntry queueEntry : messages)
+ {
+ ServerMessage serverMsg = queueEntry.getMessage();
+ AMQMessageHeader header = serverMsg.getMessageHeader();
+ String[] headerAttributes =
+ {"reply-to = " + header.getReplyTo(),
+ "propertyFlags = ",
+ "ApplicationID = " + header.getAppId(),
+ "ClusterID = ",
+ "UserId = " + header.getUserId(),
+ "JMSMessageID = " + header.getMessageId(),
+ "JMSCorrelationID = " + header.getCorrelationId(),
+ "JMSDeliveryMode = " + (serverMsg.isPersistent() ? "Persistent" : "Non_Persistent"),
+ "JMSPriority = " + header.getPriority(),
+ "JMSType = " + header.getType(),
+ "JMSExpiration = " + (header.getExpiration() == 0 ? null : FAST_DATE_FORMAT.format(header.getExpiration())),
+ "JMSTimestamp = " + (header.getTimestamp() == 0 ? null : FAST_DATE_FORMAT.format(header.getTimestamp()))
+ };
+
+ Object[] itemValues = new Object[]{ serverMsg.getMessageNumber(),
+ headerAttributes,
+ serverMsg.getSize(),
+ queueEntry.isRedelivered(),
+ position,
+ queueEntry.getDeliveryCount()};
+
+ position++;
+
+ CompositeData messageData =
+ new CompositeDataSupport(MSG_DATA_TYPE, VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC_ARRAY, itemValues);
+ messageTable.put(messageData);
+ }
+
+ return messageTable;
+
+ }
+
+ public CompositeData viewMessageContent(long messageId)
+ throws IOException, JMException
+ {
+ QueueEntry entry = getMessage(messageId);
+ if(entry == null)
+ {
+ throw new OperationsException("AMQMessage with message id = " + messageId + " is not in the " + _queue.getName());
+ }
+
+ ServerMessage serverMsg = entry.getMessage();
+ final int bodySize = (int) serverMsg.getSize();
+
+ byte[] msgContent = new byte[bodySize];
+
+ ByteBuffer buf = ByteBuffer.wrap(msgContent);
+ int stored = serverMsg.getContent(buf, 0);
+
+ if(bodySize != stored)
+ {
+ LOGGER.error(String.format("An unexpected amount of content was retrieved " +
+ "(expected %d, got %d bytes) when viewing content for message with ID %d " +
+ "on queue '%s' in virtual host '%s'",
+ bodySize, stored, messageId, _queue.getName(), _vhostMBean.getName()));
+ }
+
+ AMQMessageHeader header = serverMsg.getMessageHeader();
+
+ String mimeType = null, encoding = null;
+ if (header != null)
+ {
+ mimeType = header.getMimeType();
+
+ encoding = header.getEncoding();
+ }
+
+
+ Object[] itemValues = { messageId, mimeType, encoding, msgContent };
+
+ return new CompositeDataSupport(MSG_CONTENT_TYPE, VIEW_MSG_COMPOSIT_ITEM_NAMES_ARRAY, itemValues);
+
+
+ }
+
+ private QueueEntry getMessage(long messageId)
+ {
+ GetMessageVisitor visitor = new GetMessageVisitor(messageId);
+ _queue.visit(visitor);
+ return visitor.getEntry();
+ }
+
+ public void deleteMessageFromTop() throws IOException, JMException
+ {
+ VirtualHost vhost = _queue.getParent(VirtualHost.class);
+ vhost.executeTransaction(new VirtualHost.TransactionalOperation()
+ {
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+ _queue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ if(entry.acquire())
+ {
+ txn.dequeue(entry);
+ return true;
+ }
+ return false;
+ }
+ });
+
+ }
+ });
+
+ }
+
+ public Long clearQueue() throws IOException, JMException
+ {
+ VirtualHost vhost = _queue.getParent(VirtualHost.class);
+ final AtomicLong count = new AtomicLong();
+
+ vhost.executeTransaction(new VirtualHost.TransactionalOperation()
+ {
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+ _queue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ final ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ txn.dequeue(entry);
+ count.incrementAndGet();
+
+ }
+ return false;
+ }
+ });
+
+ }
+ });
+ return count.get();
+ }
+
+ public void moveMessages(final long fromMessageId, final long toMessageId, String toQueue)
+ throws IOException, JMException
+ {
+ if ((fromMessageId > toMessageId) || (fromMessageId < 1))
+ {
+ throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\"");
+ }
+
+ VirtualHost vhost = _queue.getParent(VirtualHost.class);
+ final Queue destinationQueue = MBeanUtils.findQueueFromQueueName(vhost, toQueue);
+
+ vhost.executeTransaction(new VirtualHost.TransactionalOperation()
+ {
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+ _queue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ final ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ final long messageId = message.getMessageNumber();
+
+ if ((messageId >= fromMessageId)
+ && (messageId <= toMessageId))
+ {
+ txn.move(entry, destinationQueue);
+ }
+
+ }
+ return false;
+ }
+ });
+ }
+ });
+ }
+
+ public void deleteMessages(final long fromMessageId, final long toMessageId)
+ throws IOException, JMException
+ {
+ VirtualHost vhost = _queue.getParent(VirtualHost.class);
+ vhost.executeTransaction(new VirtualHost.TransactionalOperation()
+ {
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+ _queue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ final ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ final long messageId = message.getMessageNumber();
+
+ if ((messageId >= fromMessageId)
+ && (messageId <= toMessageId))
+ {
+ txn.dequeue(entry);
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+ });
+ }
+
+ public void copyMessages(final long fromMessageId, final long toMessageId, String toQueue)
+ throws IOException, JMException
+ {
+ if ((fromMessageId > toMessageId) || (fromMessageId < 1))
+ {
+ throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\"");
+ }
+
+ VirtualHost vhost = _queue.getParent(VirtualHost.class);
+ final Queue destinationQueue = MBeanUtils.findQueueFromQueueName(vhost, toQueue);
+
+ vhost.executeTransaction(new VirtualHost.TransactionalOperation()
+ {
+ public void withinTransaction(final VirtualHost.Transaction txn)
+ {
+ _queue.visit(new QueueEntryVisitor()
+ {
+
+ public boolean visit(final QueueEntry entry)
+ {
+ final ServerMessage message = entry.getMessage();
+ if(message != null)
+ {
+ final long messageId = message.getMessageNumber();
+
+ if ((messageId >= fromMessageId)
+ && (messageId <= toMessageId))
+ {
+ txn.copy(entry, destinationQueue);
+ }
+
+ }
+ return false;
+ }
+ });
+ }
+ });
+ }
+
+ private List<QueueEntry> getMessages(final long first, final long last)
+ {
+ final List<QueueEntry> messages = new ArrayList<QueueEntry>((int)(last-first)+1);
+ _queue.visit(new QueueEntryVisitor()
+ {
+ private long position = 1;
+
+ public boolean visit(QueueEntry entry)
+ {
+ if(position >= first && position <= last)
+ {
+ messages.add(entry);
+ }
+ position++;
+ return position > last;
+ }
+ });
+ return messages;
+ }
+
+
+ protected static class GetMessageVisitor implements QueueEntryVisitor
+ {
+
+ private final long _messageNumber;
+ private QueueEntry _entry;
+
+ public GetMessageVisitor(long messageId)
+ {
+ _messageNumber = messageId;
+ }
+
+ public boolean visit(QueueEntry entry)
+ {
+ if(entry.getMessage().getMessageNumber() == _messageNumber)
+ {
+ _entry = entry;
+ return true;
+ }
+ return false;
+ }
+
+ public QueueEntry getEntry()
+ {
+ return _entry;
+ }
+ }
+
+ @Override
+ public void notifyClients(NotificationCheck notification, Queue queue, String notificationMsg)
+ {
+ notificationMsg = notification.name() + " " + notificationMsg;
+
+ Notification note = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this,
+ incrementAndGetSequenceNumber(), System.currentTimeMillis(), notificationMsg);
+
+ getBroadcaster().sendNotification(note);
+ }
+
+ /**
+ * returns Notifications sent by this MBean.
+ */
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED };
+ String name = MonitorNotification.class.getName();
+ String description = "Either Message count or Queue depth or Message size has reached threshold high value";
+ MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description);
+
+ return new MBeanNotificationInfo[] { info1 };
+ }
+
+ @Override
+ public String getDescription()
+ {
+ return (String) _queue.getAttribute(Queue.DESCRIPTION);
+ }
+
+ @Override
+ public void setDescription(String description)
+ {
+ _queue.setAttribute(Queue.DESCRIPTION, getDescription(), description);
+ }
+
+ private Number getStatisticValue(String name)
+ {
+ final Number statistic = (Number) _queue.getStatistics().getStatistic(name);
+ return statistic == null ? Integer.valueOf(0) : statistic;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java
new file mode 100644
index 0000000000..67d5861dec
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java
@@ -0,0 +1,89 @@
+/*
+ * 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.jmx.mbeans;
+
+import java.io.IOException;
+
+import javax.management.JMException;
+import javax.management.NotCompliantMBeanException;
+
+import org.apache.qpid.management.common.mbeans.ServerInformation;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.Broker;
+
+@MBeanDescription("Server Information Interface")
+public class ServerInformationMBean extends AbstractStatisticsGatheringMBean<Broker> implements ServerInformation
+{
+ private final Broker _broker;
+
+ public ServerInformationMBean(ManagedObjectRegistry registry, Broker broker)
+ throws NotCompliantMBeanException, JMException
+ {
+ super(ServerInformation.class, ServerInformation.TYPE, registry, broker);
+ _broker = broker;
+
+ register();
+ }
+
+ @Override
+ public String getObjectInstanceName()
+ {
+ return ServerInformation.TYPE;
+ }
+
+ @Override
+ public Integer getManagementApiMajorVersion() throws IOException
+ {
+ return QPID_JMX_API_MAJOR_VERSION;
+ }
+
+ @Override
+ public Integer getManagementApiMinorVersion() throws IOException
+ {
+ return QPID_JMX_API_MINOR_VERSION;
+ }
+
+ @Override
+ public String getBuildVersion() throws IOException
+ {
+ return (String) _broker.getAttribute(Broker.BUILD_VERSION);
+ }
+
+ @Override
+ public String getProductVersion() throws IOException
+ {
+ return (String) _broker.getAttribute(Broker.PRODUCT_VERSION);
+ }
+
+ @Override
+ public boolean isStatisticsEnabled()
+ {
+ return true;
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ // does not have a parent
+ return null;
+ }
+}
diff --git a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java
index cef4a42b64..62733168ef 100644
--- a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java
@@ -17,8 +17,14 @@
* under the License.
*
*/
-package org.apache.qpid.shutdown;
+package org.apache.qpid.server.jmx.mbeans;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.jmx.DefaultManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+
+import javax.management.JMException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -27,11 +33,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import javax.management.NotCompliantMBeanException;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.management.DefaultManagedObject;
-
/**
* Implementation of the JMX broker shutdown plugin.
*/
@@ -46,9 +47,10 @@ public class Shutdown extends DefaultManagedObject implements ShutdownMBean
private final Runnable _shutdown = new SystemExiter();
- public Shutdown() throws NotCompliantMBeanException
+ public Shutdown(ManagedObjectRegistry registry) throws JMException
{
- super(ShutdownMBean.class, ShutdownMBean.TYPE);
+ super(ShutdownMBean.class, ShutdownMBean.TYPE, registry);
+ register();
}
/** @see ShutdownMBean#shutdown() */
@@ -108,6 +110,12 @@ public class Shutdown extends DefaultManagedObject implements ShutdownMBean
EXECUTOR.schedule(_shutdown, delay, TimeUnit.MILLISECONDS);
}
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+
/**
* Shutting down the system in another thread to avoid JMX exceptions being thrown.
*/
@@ -120,9 +128,8 @@ public class Shutdown extends DefaultManagedObject implements ShutdownMBean
}
/**
- * @see org.apache.qpid.server.management.ManagedObject#getObjectInstanceName()
+ * @see ManagedObject#getObjectInstanceName()
*/
- @Override
public String getObjectInstanceName()
{
return "Shutdown";
diff --git a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java
index 5c54fb3e21..ed69c351f7 100644
--- a/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java
@@ -17,13 +17,13 @@
* under the License.
*
*/
-package org.apache.qpid.shutdown;
-
-import javax.management.MBeanOperationInfo;
+package org.apache.qpid.server.jmx.mbeans;
import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation;
import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter;
+import javax.management.MBeanOperationInfo;
+
/**
* Shutdown plugin JMX MBean interface.
*
@@ -45,7 +45,7 @@ public interface ShutdownMBean
* @param delay the number of ms to wait
*/
@MBeanOperation(name="shutdown", description="Shutdown after the specified delay (ms)", impact = MBeanOperationInfo.ACTION)
- public void shutdown(@MBeanOperationParameter(name="when", description="delay (ms)")long delay);
+ public void shutdown(@MBeanOperationParameter(name = "when", description = "delay (ms)") long delay);
/**
* Broker will be shutdown at the specified date and time.
@@ -53,5 +53,6 @@ public interface ShutdownMBean
* @param when the date and time to shutdown
*/
@MBeanOperation(name="shutdownAt", description="Shutdown at the specified date and time (yyyy/MM/dd HH:mm:ss)", impact = MBeanOperationInfo.ACTION)
- public void shutdownAt(@MBeanOperationParameter(name="when", description="shutdown date/time (yyyy/MM/dd HH:mm:ss)")String when);
+ public void shutdownAt(@MBeanOperationParameter(name = "when",
+ description = "shutdown date/time (yyyy/MM/dd HH:mm:ss)") String when);
}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java
new file mode 100644
index 0000000000..c7aade34b4
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java
@@ -0,0 +1,179 @@
+/*
+ * 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.jmx.mbeans;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.management.common.mbeans.UserManagement;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import javax.security.auth.login.AccountNotFoundException;
+
+import java.io.IOException;
+import java.util.Map;
+
+@MBeanDescription("User Management Interface")
+public class UserManagementMBean extends AMQManagedObject implements UserManagement
+{
+ private static final Logger _logger = Logger.getLogger(UserManagementMBean.class);
+
+ private PasswordCredentialManagingAuthenticationProvider _authProvider;
+
+ // Setup for the TabularType
+ private static final TabularType _userlistDataType; // Datatype for representing User Lists
+ private static final CompositeType _userDataType; // Composite type for representing User
+
+ static
+ {
+ OpenType[] userItemTypes = new OpenType[4]; // User item types.
+ userItemTypes[0] = SimpleType.STRING; // For Username
+ userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read - No longer in use
+ userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write - No longer in use
+ userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin - No longer is use
+
+ try
+ {
+ _userDataType =
+ new CompositeType("User", "User Data", COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]),
+ COMPOSITE_ITEM_DESCRIPTIONS.toArray(new String[COMPOSITE_ITEM_DESCRIPTIONS.size()]), userItemTypes);
+
+ _userlistDataType = new TabularType("Users", "List of users", _userDataType, TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()]));
+ }
+ catch (OpenDataException e)
+ {
+ _logger.error("Tabular data setup for viewing users incorrect.", e);
+ throw new ExceptionInInitializerError("Tabular data setup for viewing users incorrect");
+ }
+ }
+
+ public UserManagementMBean(PasswordCredentialManagingAuthenticationProvider provider, ManagedObjectRegistry registry) throws JMException
+ {
+ super(UserManagement.class, UserManagement.TYPE, registry);
+ register();
+ _authProvider = provider;
+ }
+
+ @Override
+ public String getObjectInstanceName()
+ {
+ return UserManagement.TYPE;
+ }
+
+ @Override
+ public boolean setPassword(String username, String password)
+ {
+ try
+ {
+ _authProvider.setPassword(username, password);
+ }
+ catch (AccountNotFoundException e)
+ {
+ _logger.warn("Attempt to set password of non-existent user '" + username + "'");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean createUser(String username, String password)
+ {
+ return _authProvider.createUser(username, password, null);
+ }
+
+ @Override
+ public boolean deleteUser(String username)
+ {
+ try
+ {
+ _authProvider.deleteUser(username);
+ }
+ catch (AccountNotFoundException e)
+ {
+ _logger.warn("Attempt to delete user (" + username + ") that doesn't exist");
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean reloadData()
+ {
+ try
+ {
+ _authProvider.reload();
+ return true;
+ }
+ catch (IOException e)
+ {
+ _logger.error("Unable to reload user data", e);
+ return false;
+ }
+ }
+
+ @Override
+ public TabularData viewUsers()
+ {
+ Map<String, Map<String, String>> users = _authProvider.getUsers();
+
+ TabularDataSupport userList = new TabularDataSupport(_userlistDataType);
+
+ try
+ {
+ // Create the tabular list of message header contents
+ for (String user : users.keySet())
+ {
+ // Create header attributes list
+ // Read,Write,Admin items are deprecated and we return always false.
+ Object[] itemData = {user, false, false, false};
+ CompositeData messageData = new CompositeDataSupport(_userDataType, COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData);
+ userList.put(messageData);
+ }
+ }
+ catch (OpenDataException e)
+ {
+ _logger.warn("Unable to create user list due to :", e);
+ return null;
+ }
+
+ return userList;
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
new file mode 100644
index 0000000000..6990a40dee
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
@@ -0,0 +1,211 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.jmx.AMQManagedObject;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.ConfigurationChangeListener;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.virtualhost.ManagedVirtualHost;
+
+import javax.management.JMException;
+import javax.management.ObjectName;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost, ConfigurationChangeListener
+{
+ private static final Logger LOGGER = Logger.getLogger(VirtualHostMBean.class);
+
+ private final VirtualHost _virtualHost;
+
+ private final Map<ConfiguredObject, AMQManagedObject> _children =
+ new HashMap<ConfiguredObject, AMQManagedObject>();
+ private VirtualHostManagerMBean _managerMBean;
+
+ public VirtualHostMBean(VirtualHost virtualHost, ManagedObjectRegistry registry) throws JMException
+ {
+ super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE, registry);
+ _virtualHost = virtualHost;
+ virtualHost.addChangeListener(this);
+
+ initQueues();
+ initExchanges();
+ initConnections();
+
+ //This is the actual JMX bean for this 'VirtualHostMBean', leave it alone.
+ _managerMBean = new VirtualHostManagerMBean(this);
+ }
+
+ private void initQueues() throws JMException
+ {
+ synchronized (_children)
+ {
+ for(Queue queue : _virtualHost.getQueues())
+ {
+ if(!_children.containsKey(queue))
+ {
+ _children.put(queue, new QueueMBean(queue, this));
+ }
+ }
+ }
+ }
+
+ private void initExchanges() throws JMException
+ {
+ synchronized (_children)
+ {
+ for(Exchange exchange : _virtualHost.getExchanges())
+ {
+ if(!_children.containsKey(exchange))
+ {
+ _children.put(exchange, new ExchangeMBean(exchange, this));
+ }
+ }
+ }
+ }
+
+ private void initConnections() throws JMException
+ {
+ synchronized (_children)
+ {
+ for(Connection conn : _virtualHost.getConnections())
+ {
+ if(!_children.containsKey(conn))
+ {
+ _children.put(conn, new ConnectionMBean(conn, this));
+ }
+ }
+ }
+ }
+
+ public String getObjectInstanceName()
+ {
+ return ObjectName.quote(_virtualHost.getName());
+ }
+
+ public String getName()
+ {
+ return _virtualHost.getName();
+ }
+
+ public void stateChanged(ConfiguredObject object, State oldState, State newState)
+ {
+ // ignore
+ }
+
+ public void childAdded(ConfiguredObject object, ConfiguredObject child)
+ {
+ synchronized (_children)
+ {
+ try
+ {
+ if(child instanceof Queue)
+ {
+ QueueMBean queueMB = new QueueMBean((Queue)child, this);
+ _children.put(child, queueMB);
+
+ }
+ else if(child instanceof Exchange)
+ {
+ ExchangeMBean exchangeMBean = new ExchangeMBean((Exchange)child, this);
+ _children.put(child, exchangeMBean);
+
+ }
+ else if(child instanceof Connection)
+ {
+ ConnectionMBean connectionMBean = new ConnectionMBean((Connection)child, this);
+ _children.put(child, connectionMBean);
+
+ }
+ else
+ {
+ LOGGER.debug("Unsupported child : " + child.getName() + " type : " + child.getClass());
+ }
+
+ }
+ catch(JMException e)
+ {
+ LOGGER.error("Failed to add mbean for child : " + child.getName(), e);
+ }
+ }
+ }
+
+ public void childRemoved(ConfiguredObject object, ConfiguredObject child)
+ {
+ synchronized (_children)
+ {
+ AMQManagedObject mbean = _children.remove(child);
+ if(mbean != null)
+ {
+ try
+ {
+ mbean.unregister();
+ }
+ catch(JMException e)
+ {
+ LOGGER.error("Failed to remove mbean for child : " + child.getName(), e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+
+ protected VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+ public Collection<QueueMBean> getQueues()
+ {
+ Collection<AMQManagedObject> children;
+ synchronized (_children)
+ {
+ children = new ArrayList<AMQManagedObject>(_children.values());
+ }
+ Collection<QueueMBean> queues = new ArrayList<QueueMBean>();
+
+ for(AMQManagedObject child : children)
+ {
+ if(child instanceof QueueMBean)
+ {
+ queues.add((QueueMBean) child);
+ }
+ }
+
+ return queues;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java
new file mode 100644
index 0000000000..b3dbbc424a
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java
@@ -0,0 +1,238 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
+import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter;
+import org.apache.qpid.server.jmx.ManagedObject;
+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.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+
+@MBeanDescription("This MBean exposes the broker level management features")
+public class VirtualHostManagerMBean extends AbstractStatisticsGatheringMBean<VirtualHost> implements ManagedBroker
+{
+ private static final Logger LOGGER = Logger.getLogger(VirtualHostManagerMBean.class);
+
+ private static final boolean _moveNonExclusiveQueueOwnerToDescription = Boolean.parseBoolean(System.getProperty("qpid.move_non_exclusive_queue_owner_to_description", Boolean.TRUE.toString()));
+
+ private final VirtualHostMBean _virtualHostMBean;
+
+ @MBeanConstructor("Creates the Broker Manager MBean")
+ public VirtualHostManagerMBean(VirtualHostMBean virtualHostMBean) throws JMException
+ {
+ super(ManagedBroker.class, ManagedBroker.TYPE, virtualHostMBean.getRegistry(), virtualHostMBean.getVirtualHost());
+ _virtualHostMBean = virtualHostMBean;
+ register();
+ }
+
+
+ @Override
+ public String getObjectInstanceName()
+ {
+ return ObjectName.quote(_virtualHostMBean.getName());
+ }
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return _virtualHostMBean;
+ }
+
+ @Override
+ public String[] getExchangeTypes() throws IOException
+ {
+ Collection<String> exchangeTypes = _virtualHostMBean.getVirtualHost().getExchangeTypes();
+ return exchangeTypes.toArray(new String[exchangeTypes.size()]);
+ }
+
+ @Override
+ public List<String> retrieveQueueAttributeNames() throws IOException
+ {
+ return ManagedQueue.QUEUE_ATTRIBUTES;
+ }
+
+ @Override
+ public List<List<Object>> retrieveQueueAttributeValues(
+ @MBeanOperationParameter(name = "attributes", description = "Attributes to retrieve") String[] attributes)
+ throws IOException
+ {
+ int attributesLength = attributes.length;
+
+ List<List<Object>> queueAttributesList = new ArrayList<List<Object>>();
+
+ for(QueueMBean queue : _virtualHostMBean.getQueues())
+ {
+
+ if(queue == null)
+ {
+ continue;
+ }
+
+ List<Object> attributeValues = new ArrayList<Object>(attributesLength);
+
+ for(int i=0; i < attributesLength; i++)
+ {
+ try
+ {
+ attributeValues.add(queue.getAttribute(attributes[i]));
+ }
+ catch (Exception e)
+ {
+ attributeValues.add("-");
+ }
+ }
+
+ queueAttributesList.add(attributeValues);
+ }
+
+ return queueAttributesList;
+
+ }
+
+ @Override
+ public void createNewExchange(String name, String type, boolean durable)
+ throws IOException, JMException, MBeanException
+ {
+ if (!getConfiguredObject().getExchangeTypes().contains(type))
+ {
+ throw new OperationsException("No such exchange type \""+type+"\"");
+ }
+
+ try
+ {
+ getConfiguredObject().createExchange(name, State.ACTIVE, durable,
+ LifetimePolicy.PERMANENT, 0l, type, Collections.EMPTY_MAP);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ JMException jme = new JMException(iae.toString());
+ throw new MBeanException(jme, "Error in creating exchange " + name);
+ }
+
+ }
+
+ @Override
+ public void unregisterExchange(String exchangeName)
+ throws IOException, JMException, MBeanException
+ {
+ Exchange theExchange = MBeanUtils.findExchangeFromExchangeName(_virtualHostMBean.getVirtualHost(), exchangeName);
+ try
+ {
+ theExchange.delete();
+ }
+ catch (IllegalStateException ex)
+ {
+ final JMException jme = new JMException(ex.toString());
+ throw new MBeanException(jme, "Error in unregistering exchange " + exchangeName);
+ }
+ }
+
+ @Override
+ public void createNewQueue(String queueName, String owner, boolean durable)
+ throws IOException, JMException, MBeanException
+ {
+ createNewQueue(queueName, owner, durable, Collections.EMPTY_MAP);
+ }
+
+ @Override
+ public void createNewQueue(String queueName, String owner, boolean durable, Map<String, Object> originalArguments)
+ throws IOException, JMException
+ {
+ final Map<String, Object> createArgs = processNewQueueArguments(queueName, owner, originalArguments);
+ getConfiguredObject().createQueue(queueName, State.ACTIVE, durable, false, LifetimePolicy.PERMANENT, 0l, createArgs);
+ }
+
+
+ /**
+ * Some users have been abusing the owner field to store a queue description. As the owner field
+ * only makes sense if exclusive=true, and it is currently impossible to create an exclusive queue via
+ * the JMX interface, if the user specifies a owner, then we assume that they actually mean to pass a description.
+ */
+ private Map<String, Object> processNewQueueArguments(String queueName,
+ String owner, Map<String, Object> arguments)
+ {
+ final Map<String, Object> argumentsCopy;
+ if (_moveNonExclusiveQueueOwnerToDescription && owner != null)
+ {
+ argumentsCopy = new HashMap<String, Object>(arguments == null ? new HashMap<String, Object>() : arguments);
+ if (!argumentsCopy.containsKey(AMQQueueFactory.X_QPID_DESCRIPTION))
+ {
+ LOGGER.warn("Non-exclusive owner " + owner + " for new queue " + queueName + " moved to " + AMQQueueFactory.X_QPID_DESCRIPTION);
+
+ argumentsCopy.put(AMQQueueFactory.X_QPID_DESCRIPTION, owner);
+ }
+ else
+ {
+ LOGGER.warn("Non-exclusive owner " + owner + " for new queue " + queueName + " ignored.");
+ }
+ }
+ else
+ {
+ argumentsCopy = arguments;
+ }
+ return argumentsCopy;
+ }
+
+ @Override
+ public void deleteQueue(
+ @MBeanOperationParameter(name = ManagedQueue.TYPE, description = "Queue Name") String queueName)
+ throws IOException, JMException, MBeanException
+ {
+ Queue theQueue = MBeanUtils.findQueueFromQueueName(_virtualHostMBean.getVirtualHost(), queueName);
+ theQueue.delete();
+ }
+
+ @Override
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ return getObjectNameForSingleInstanceMBean();
+ }
+
+ public synchronized boolean isStatisticsEnabled()
+ {
+ updateStats();
+ return false; //TODO - implement isStatisticsEnabled
+ }
+
+}
diff --git a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/Activator.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java
index fa0ffb5045..a2631bab7f 100644
--- a/java/broker-plugins/extras/src/main/java/org/apache/qpid/extras/Activator.java
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,32 +18,29 @@
* under the License.
*
*/
-package org.apache.qpid.extras;
+package org.apache.qpid.server.jmx;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
+import javax.management.JMException;
-import org.apache.qpid.extras.exchanges.diagnostic.DiagnosticExchangeType;
-import org.apache.qpid.extras.exchanges.example.TestExchangeType;
-import org.apache.qpid.server.exchange.ExchangeType;
+public class NoopManagedObjectRegistry implements ManagedObjectRegistry
+{
+ public NoopManagedObjectRegistry()
+ {
+ }
-/**
- *
- * @author aidan
- *
- * Dummy class, used by PluginTest
- */
+ public void start()
+ {
+ }
-public class Activator implements BundleActivator
-{
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ }
- public void start(BundleContext ctx) throws Exception
+ public void unregisterObject(ManagedObject managedObject) throws JMException
{
- ctx.registerService(ExchangeType.class.getName(), new TestExchangeType(), null);
- ctx.registerService(ExchangeType.class.getName(), new DiagnosticExchangeType(), null);
}
- public void stop(BundleContext ctx) throws Exception
+ public void close()
{
}
}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java
new file mode 100644
index 0000000000..0c5fb0bf1f
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Date;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.server.jmx.ManagedObject;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Session;
+import org.apache.qpid.server.model.Statistics;
+
+public class ConnectionMBeanTest extends TestCase
+{
+ private ConnectionMBean _connectionMBean;
+ private Connection _mockConnection;
+ private VirtualHostMBean _mockVirtualHostMBean;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockConnection = mock(Connection.class);
+ _mockVirtualHostMBean = mock(VirtualHostMBean.class);
+
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+ when(_mockVirtualHostMBean.getRegistry()).thenReturn(_mockManagedObjectRegistry);
+
+ _connectionMBean = new ConnectionMBean(_mockConnection, _mockVirtualHostMBean);
+ }
+
+ public void testMBeanRegistersItself() throws Exception
+ {
+ ConnectionMBean connectionMBean = new ConnectionMBean(_mockConnection, _mockVirtualHostMBean);
+ verify(_mockManagedObjectRegistry).registerObject(connectionMBean);
+ }
+
+ public void testCloseConnection() throws Exception
+ {
+ _connectionMBean.closeConnection();
+ verify(_mockConnection).delete();
+ }
+
+ public void testCommitTransactions()
+ {
+ try
+ {
+ _connectionMBean.commitTransactions(0);
+ fail("Exception not thrown");
+ }
+ catch(JMException e)
+ {
+ assertTrue("Cause should be an UnsupportedOperationException", e.getCause() instanceof UnsupportedOperationException);
+ }
+ }
+
+ public void testRollbackTransactions()
+ {
+ try
+ {
+ _connectionMBean.rollbackTransactions(0);
+ fail("Exception not thrown");
+ }
+ catch(JMException e)
+ {
+ assertTrue("Cause should be an UnsupportedOperationException", e.getCause() instanceof UnsupportedOperationException);
+ }
+ }
+
+ public void testChannelsWithSingleTransactionalSession() throws Exception
+ {
+ int channelId = 10;
+ int unacknowledgedMessages = 2;
+ long localTransactionBegins = 1;
+ boolean transactional = true;
+ boolean blocked = false;
+
+ Session mockSession = createMockedSession(channelId, unacknowledgedMessages, localTransactionBegins, blocked);
+
+ when(_mockConnection.getSessions()).thenReturn(Collections.singletonList(mockSession));
+
+ TabularData table = _connectionMBean.channels();
+ assertEquals("Unexpected number of rows in table", 1, table.size());
+
+ final CompositeData row = table.get(new Integer[] {channelId} );
+ assertChannelRow(row, channelId, unacknowledgedMessages, transactional, blocked);
+ }
+
+ public void testChannelsWithSingleNonTransactionalSession() throws Exception
+ {
+ int channelId = 10;
+ int unacknowledgedMessages = 2;
+ long localTransactionBegins = 0;
+ boolean transactional = false;
+ boolean blocked = false;
+
+ Session mockSession = createMockedSession(channelId, unacknowledgedMessages, localTransactionBegins, blocked);
+
+ when(_mockConnection.getSessions()).thenReturn(Collections.singletonList(mockSession));
+
+ TabularData table = _connectionMBean.channels();
+ assertEquals("Unexpected number of rows in table", 1, table.size());
+
+ final CompositeData row = table.get(new Integer[] {channelId} );
+ assertChannelRow(row, channelId, unacknowledgedMessages, transactional, blocked);
+ }
+
+ public void testChannelsWithSessionBlocked() throws Exception
+ {
+ int channelId = 10;
+ int unacknowledgedMessages = 2;
+ long localTransactionBegins = 0;
+ boolean transactional = false;
+ boolean blocked = true;
+
+ Session mockSession = createMockedSession(channelId, unacknowledgedMessages, localTransactionBegins, blocked);
+
+ when(_mockConnection.getSessions()).thenReturn(Collections.singletonList(mockSession));
+
+ TabularData table = _connectionMBean.channels();
+ assertEquals("Unexpected number of rows in table", 1, table.size());
+
+ final CompositeData row = table.get(new Integer[] {channelId} );
+ assertChannelRow(row, channelId, unacknowledgedMessages, transactional, blocked);
+ }
+
+ public void testParentObjectIsVirtualHost()
+ {
+ ManagedObject parent = _connectionMBean.getParentObject();
+ assertEquals(_mockVirtualHostMBean, parent);
+ }
+
+ public void testGetObjectInstanceName()
+ {
+ String remoteAddress = "testRemoteAddress";
+ String quotedRemoteAddress = "\"testRemoteAddress\"";
+ when(_mockConnection.getAttribute(Connection.REMOTE_ADDRESS)).thenReturn(remoteAddress);
+ String objectInstanceName = _connectionMBean.getObjectInstanceName();
+ assertEquals(quotedRemoteAddress, objectInstanceName);
+ }
+
+ public void testGetAuthorizedId() throws Exception
+ {
+ assertAttribute("authorizedId", "testAuthorizedId", Connection.PRINCIPAL);
+ }
+
+ public void testGetClientId() throws Exception
+ {
+ assertAttribute("clientId", "testClientId", Connection.CLIENT_ID);
+ }
+
+ public void testGetVersion() throws Exception
+ {
+ assertAttribute("version", "testVersion", Connection.CLIENT_VERSION);
+ }
+
+ public void testGetRemoteAddress() throws Exception
+ {
+ assertAttribute("remoteAddress", "testRemoteAddress", Connection.REMOTE_ADDRESS);
+ }
+
+ public void testGetLastIoTime()
+ {
+ Statistics mockStatistics = mock(Statistics.class);
+ when(_mockConnection.getStatistics()).thenReturn(mockStatistics);
+ when(mockStatistics.getStatistic(Connection.LAST_IO_TIME)).thenReturn(1L);
+
+ Object actualValue = _connectionMBean.getLastIoTime();
+ assertEquals("Unexpected lastIoTime", new Date(1L), actualValue);
+ }
+
+ public void testGetMaximumNumberOfChannels() throws Exception
+ {
+ assertAttribute("maximumNumberOfChannels", 10l, Connection.SESSION_COUNT_LIMIT);
+ }
+
+ public void testIsStatisticsEnabledAlwaysTrue() throws Exception
+ {
+ assertTrue(_connectionMBean.isStatisticsEnabled());
+ }
+
+ private void assertAttribute(String jmxAttributeName, Object expectedValue, String underlyingAttributeName) throws Exception
+ {
+ when(_mockConnection.getAttribute(underlyingAttributeName)).thenReturn(expectedValue);
+ MBeanTestUtils.assertMBeanAttribute(_connectionMBean, jmxAttributeName, expectedValue);
+ }
+
+ private void assertChannelRow(final CompositeData row, int channelId, int unacknowledgedMessages, boolean isTransactional, boolean flowBlocked)
+ {
+ assertNotNull("No row for channel id " + channelId, row);
+ assertEquals("Unexpected channel id", channelId, row.get(ManagedConnection.CHAN_ID));
+ assertEquals("Unexpected transactional flag", isTransactional, row.get(ManagedConnection.TRANSACTIONAL));
+ assertEquals("Unexpected unacknowledged message count", unacknowledgedMessages, row.get(ManagedConnection.UNACKED_COUNT));
+ assertEquals("Unexpected flow blocked", flowBlocked, row.get(ManagedConnection.FLOW_BLOCKED));
+ }
+
+ private Session createMockedSession(int channelId, int unacknowledgedMessages, long localTransactionBegins, boolean blocked)
+ {
+ Session mockSession = mock(Session.class);
+ Statistics mockSessionStatistics = mock(Statistics.class);
+ when(mockSessionStatistics.getStatistic(Session.LOCAL_TRANSACTION_BEGINS)).thenReturn(localTransactionBegins);
+ when(mockSessionStatistics.getStatistic(Session.UNACKNOWLEDGED_MESSAGES)).thenReturn(unacknowledgedMessages);
+
+ when(mockSession.getStatistics()).thenReturn(mockSessionStatistics);
+ when(mockSession.getAttribute(Session.CHANNEL_ID)).thenReturn(channelId);
+ when(mockSession.getAttribute(Session.PRODUCER_FLOW_BLOCKED)).thenReturn(blocked);
+ return mockSession;
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBeanTest.java
new file mode 100644
index 0000000000..e350f80a25
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBeanTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.anyMap;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.management.JMException;
+import javax.management.ListenerNotFoundException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.OperationsException;
+
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.Binding;
+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.Statistics;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.NotificationCheck;
+import org.mockito.ArgumentMatcher;
+
+import junit.framework.TestCase;
+
+public class ExchangeMBeanTest extends TestCase
+{
+ private static final String EXCHANGE_NAME = "EXCHANGE_NAME";
+ private static final String EXCHANGE_TYPE = "EXCHANGE_TYPE";
+ private static final String QUEUE1_NAME = "QUEUE1_NAME";
+ private static final String QUEUE2_NAME = "QUEUE2_NAME";
+ private static final String BINDING1 = "BINDING1";
+ private static final String BINDING2 = "BINDING2";
+
+ private Exchange _mockExchange;
+ private VirtualHostMBean _mockVirtualHostMBean;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+ private ExchangeMBean _exchangeMBean;
+ private Queue _mockQueue1;
+ private Queue _mockQueue2;
+ private Exchange _mockHeadersExchange;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockExchange = mock(Exchange.class);
+ when(_mockExchange.getName()).thenReturn(EXCHANGE_NAME);
+ when(_mockExchange.getExchangeType()).thenReturn(EXCHANGE_TYPE);
+ _mockVirtualHostMBean = mock(VirtualHostMBean.class);
+
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+ when(_mockVirtualHostMBean.getRegistry()).thenReturn(_mockManagedObjectRegistry);
+
+ _mockQueue1 = createMockQueue(QUEUE1_NAME);
+ _mockQueue2 = createMockQueue(QUEUE2_NAME);
+
+ VirtualHost mockVirtualHost = mock(VirtualHost.class);
+ when(mockVirtualHost.getQueues()).thenReturn(Arrays.asList(new Queue[] {_mockQueue1, _mockQueue2}));
+ when(_mockExchange.getParent(VirtualHost.class)).thenReturn(mockVirtualHost);
+
+ _exchangeMBean = new ExchangeMBean(_mockExchange, _mockVirtualHostMBean);
+
+ _mockHeadersExchange = mock(Exchange.class);
+ when(_mockHeadersExchange.getExchangeType()).thenReturn(ExchangeMBean.HEADERS_EXCHANGE_TYPE);
+ when(_mockHeadersExchange.getParent(VirtualHost.class)).thenReturn(mockVirtualHost);
+
+ }
+
+ public void testExchangeName()
+ {
+ assertEquals(EXCHANGE_NAME, _exchangeMBean.getName());
+ }
+
+ public void testExchangeType()
+ {
+ assertEquals(EXCHANGE_TYPE, _exchangeMBean.getExchangeType());
+ }
+
+ public void testNonHeadersExchangeCreateNewBinding() throws Exception
+ {
+ _exchangeMBean.createNewBinding(QUEUE1_NAME, BINDING1);
+ verify(_mockExchange).createBinding(BINDING1, _mockQueue1, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
+ }
+
+ public void testCreateNewBindingWhereQueueIsUnknown() throws Exception
+ {
+ try
+ {
+ _exchangeMBean.createNewBinding("unknown", BINDING1);
+ }
+ catch (OperationsException oe)
+ {
+ // PASS
+ assertEquals("No such queue \"unknown\"", oe.getMessage());
+ }
+ verify(_mockExchange, never()).createBinding(anyString(), any(Queue.class), anyMap(), anyMap());
+ }
+
+ public void testRemoveBinding() throws Exception
+ {
+ Binding mockBinding1 = createBindingOnQueue(BINDING1, _mockQueue1);
+ Binding mockBinding2 = createBindingOnQueue(BINDING2, _mockQueue1);
+ when(_mockExchange.getBindings()).thenReturn(Arrays.asList(new Binding[] {mockBinding1, mockBinding2}));
+
+ _exchangeMBean.removeBinding(QUEUE1_NAME, BINDING1);
+ verify(mockBinding1).delete();
+ }
+
+ public void testRemoveBindingWhereQueueIsUnknown() throws Exception
+ {
+ Binding mockBinding1 = createBindingOnQueue(BINDING1, _mockQueue1);
+ when(_mockExchange.getBindings()).thenReturn(Arrays.asList(new Binding[] {mockBinding1}));
+
+ try
+ {
+ _exchangeMBean.removeBinding("unknown", BINDING1);
+ fail("Exception not thrown");
+ }
+ catch (OperationsException oe)
+ {
+ // PASS
+ assertEquals("No such queue \"unknown\"", oe.getMessage());
+ }
+ verify(mockBinding1, never()).delete();
+ }
+
+ public void testRemoveBindingWhereBindingNameIsUnknown() throws Exception
+ {
+ Binding mockBinding1 = createBindingOnQueue(BINDING1, _mockQueue1);
+ when(_mockExchange.getBindings()).thenReturn(Arrays.asList(new Binding[] {mockBinding1}));
+
+ try
+ {
+ _exchangeMBean.removeBinding(QUEUE1_NAME, "unknown");
+ fail("Exception not thrown");
+ }
+ catch (OperationsException oe)
+ {
+ // PASS
+ assertEquals("No such binding \"unknown\" on queue \"" + QUEUE1_NAME + "\"", oe.getMessage());
+ }
+ verify(mockBinding1, never()).delete();
+ }
+
+ public void testHeadersExchangeCreateNewBinding() throws Exception
+ {
+ String binding = "key1=binding1,key2=binding2";
+ Map<String, Object> expectedBindingMap = new HashMap<String, Object>()
+ {{
+ put("key1", "binding1");
+ put("key2", "binding2");
+ }};
+ _exchangeMBean = new ExchangeMBean(_mockHeadersExchange, _mockVirtualHostMBean);
+
+ _exchangeMBean.createNewBinding(QUEUE1_NAME, binding);
+ verify(_mockHeadersExchange).createBinding(binding, _mockQueue1, expectedBindingMap, Collections.EMPTY_MAP);
+ }
+
+ public void testHeadersExchangeCreateNewBindingWithFieldWithoutValue() throws Exception
+ {
+ String binding = "key1=binding1,key2=";
+ Map<String, Object> expectedBindingMap = new HashMap<String, Object>()
+ {{
+ put("key1", "binding1");
+ put("key2", "");
+ }};
+ _exchangeMBean = new ExchangeMBean(_mockHeadersExchange, _mockVirtualHostMBean);
+
+ _exchangeMBean.createNewBinding(QUEUE1_NAME, binding);
+ verify(_mockHeadersExchange).createBinding(binding, _mockQueue1, expectedBindingMap, Collections.EMPTY_MAP);
+ }
+
+ public void testHeadersExchangeCreateNewBindingMalformed() throws Exception
+ {
+ String binding = "=binding1,=";
+ _exchangeMBean = new ExchangeMBean(_mockHeadersExchange, _mockVirtualHostMBean);
+
+ try
+ {
+ _exchangeMBean.createNewBinding(QUEUE1_NAME, binding);
+ fail("Exception not thrown");
+ }
+ catch (JMException e)
+ {
+ assertEquals("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\"", e.getMessage());
+ }
+ }
+
+ private Binding createBindingOnQueue(String bindingName, Queue queue)
+ {
+ Binding mockBinding = mock(Binding.class);
+ when(mockBinding.getParent(Queue.class)).thenReturn(queue);
+ when(mockBinding.getName()).thenReturn(bindingName);
+ return mockBinding;
+ }
+
+ private Queue createMockQueue(String queueName)
+ {
+ Queue mockQueue = mock(Queue.class);
+ when(mockQueue.getName()).thenReturn(queueName);
+ return mockQueue;
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java
new file mode 100644
index 0000000000..ae1be5db00
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+package org.apache.qpid.server.jmx.mbeans;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.anyString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
+
+public class LoggingManagementMBeanTest extends TestCase
+{
+ private static final String TEST_LEVEL1 = "LEVEL1";
+ private static final String TEST_LEVEL2 = "LEVEL2";
+
+ private LoggingManagementMBean _loggingMBean;
+ private LoggingFacade _mockLoggingFacade;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockLoggingFacade = mock(LoggingFacade.class);
+ final List<String> listOfLevels = new ArrayList<String>()
+ {{
+ add(TEST_LEVEL1);
+ add(TEST_LEVEL2);
+ }};
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(listOfLevels);
+
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+
+ _loggingMBean = new LoggingManagementMBean(_mockLoggingFacade, _mockManagedObjectRegistry);
+ }
+
+ public void testMBeanRegistersItself() throws Exception
+ {
+ LoggingManagementMBean connectionMBean = new LoggingManagementMBean(_mockLoggingFacade, _mockManagedObjectRegistry);
+ verify(_mockManagedObjectRegistry).registerObject(connectionMBean);
+ }
+
+ public void testLog4jLogWatchInterval() throws Exception
+ {
+ final Integer value = 5000;
+ when(_mockLoggingFacade.getLog4jLogWatchInterval()).thenReturn(value);
+
+ assertEquals("Unexpected watch interval",value, _loggingMBean.getLog4jLogWatchInterval());
+ }
+
+ public void testGetAvailableLoggerLevels() throws Exception
+ {
+ String[] actualLevels = _loggingMBean.getAvailableLoggerLevels();
+ assertEquals(3, actualLevels.length);
+ assertEquals(TEST_LEVEL1, actualLevels[0]);
+ assertEquals(TEST_LEVEL2, actualLevels[1]);
+ assertEquals(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL, actualLevels[2]);
+ }
+
+ public void testViewEffectiveRuntimeLoggerLevels() throws Exception
+ {
+ Map<String, String> loggerLevels = new TreeMap<String, String>();
+ loggerLevels.put("a.b.D", TEST_LEVEL2);
+ loggerLevels.put("a.b.C", TEST_LEVEL1);
+ loggerLevels.put("a.b.c.E", TEST_LEVEL2);
+
+ when(_mockLoggingFacade.retrieveRuntimeLoggersLevels()).thenReturn(loggerLevels );
+
+ TabularData table = _loggingMBean.viewEffectiveRuntimeLoggerLevels();
+ assertEquals(3, table.size());
+
+ final CompositeData row1 = table.get(new String[] {"a.b.C"} );
+ final CompositeData row2 = table.get(new String[] {"a.b.D"} );
+ final CompositeData row3 = table.get(new String[] {"a.b.c.E"} );
+ assertChannelRow(row1, "a.b.C", TEST_LEVEL1);
+ assertChannelRow(row2, "a.b.D", TEST_LEVEL2);
+ assertChannelRow(row3, "a.b.c.E", TEST_LEVEL2);
+ }
+
+ public void testGetRuntimeRootLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.retrieveRuntimeRootLoggerLevel()).thenReturn(TEST_LEVEL1);
+
+ assertEquals(TEST_LEVEL1, _loggingMBean.getRuntimeRootLoggerLevel());
+ }
+
+ public void testSetRuntimeRootLoggerLevel() throws Exception
+ {
+ _loggingMBean.setRuntimeRootLoggerLevel(TEST_LEVEL1);
+ verify(_mockLoggingFacade).setRuntimeRootLoggerLevel(TEST_LEVEL1);
+ }
+
+ public void testSetRuntimeRootLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeRootLoggerLevel("unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeRootLoggerLevel("unknown");
+ }
+
+ public void testSetRuntimeRootLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeRootLoggerLevel(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeRootLoggerLevel(anyString());
+ }
+
+ public void testSetRuntimeLoggerLevel() throws Exception
+ {
+ _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ verify(_mockLoggingFacade).setRuntimeLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ }
+
+ public void testSetRuntimeLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", "unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeLoggerLevel(anyString(), anyString());
+ }
+
+ public void testSetRuntimeLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertTrue(result);
+ verify(_mockLoggingFacade).setRuntimeLoggerLevel("a.b.c.D", null);
+ }
+
+ public void testViewEffectiveConfigFileLoggerLevels() throws Exception
+ {
+ Map<String, String> loggerLevels = new TreeMap<String, String>();
+ loggerLevels.put("a.b.D", "level2");
+ loggerLevels.put("a.b.C", TEST_LEVEL1);
+ loggerLevels.put("a.b.c.E", "level2");
+
+ when(_mockLoggingFacade.retrieveConfigFileLoggersLevels()).thenReturn(loggerLevels );
+
+ TabularData table = _loggingMBean.viewConfigFileLoggerLevels();
+ assertEquals(3, table.size());
+
+ final CompositeData row1 = table.get(new String[] {"a.b.C"} );
+ final CompositeData row2 = table.get(new String[] {"a.b.D"} );
+ final CompositeData row3 = table.get(new String[] {"a.b.c.E"} );
+ assertChannelRow(row1, "a.b.C", TEST_LEVEL1);
+ assertChannelRow(row2, "a.b.D", TEST_LEVEL2);
+ assertChannelRow(row3, "a.b.c.E", TEST_LEVEL2);
+ }
+
+ public void testGetConfigFileRootLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.retrieveConfigFileRootLoggerLevel()).thenReturn(TEST_LEVEL1);
+
+ assertEquals(TEST_LEVEL1, _loggingMBean.getConfigFileRootLoggerLevel());
+ }
+
+ public void testSetConfigFileRootLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ _loggingMBean.setConfigFileRootLoggerLevel(TEST_LEVEL1);
+ verify(_mockLoggingFacade).setConfigFileRootLoggerLevel(TEST_LEVEL1);
+ }
+
+ public void testSetConfigFileRootLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileRootLoggerLevel("unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileRootLoggerLevel("unknown");
+ }
+
+ public void testSetConfigFileRootLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileRootLoggerLevel(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileRootLoggerLevel(anyString());
+ }
+
+ public void testSetConfigFileLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ verify(_mockLoggingFacade).setConfigFileLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ }
+
+ public void testSetConfigFileLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", "unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileLoggerLevel("a.b.c.D", "unknown");
+ }
+
+ public void testSetConfigFileLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertTrue(result);
+ verify(_mockLoggingFacade).setConfigFileLoggerLevel("a.b.c.D", null);
+ }
+
+ public void testReloadConfigFile() throws Exception
+ {
+ _loggingMBean.reloadConfigFile();
+
+ verify(_mockLoggingFacade).reload();
+ }
+
+ private void assertChannelRow(final CompositeData row, String logger, String level)
+ {
+ assertNotNull("No row for " + logger, row);
+ assertEquals("Unexpected logger name", logger, row.get(LoggingManagement.LOGGER_NAME));
+ assertEquals("Unexpected level", level, row.get(LoggingManagement.LOGGER_LEVEL));
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java
new file mode 100644
index 0000000000..5f913e5f33
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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.jmx.mbeans;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.qpid.server.jmx.DefaultManagedObject;
+
+public class MBeanTestUtils
+{
+
+ public static void assertMBeanAttribute(DefaultManagedObject managedObject, String jmxAttributeName, Object expectedValue) throws Exception
+ {
+ Object actualValue = PropertyUtils.getSimpleProperty(managedObject, jmxAttributeName);
+ TestCase.assertEquals("Attribute " + jmxAttributeName + " has unexpected value", expectedValue, actualValue);
+ }
+
+ public static void setMBeanAttribute(DefaultManagedObject managedObject, String jmxAttributeName, Object newValue) throws Exception
+ {
+ PropertyUtils.setSimpleProperty(managedObject, jmxAttributeName, newValue);
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java
new file mode 100644
index 0000000000..f2663bca4e
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java
@@ -0,0 +1,439 @@
+/*
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Matchers.argThat;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.OperationsException;
+import javax.management.openmbean.CompositeDataSupport;
+
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.jmx.mbeans.QueueMBean.GetMessageVisitor;
+import org.apache.qpid.server.message.ServerMessage;
+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.Statistics;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.NotificationCheck;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Matchers;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class QueueMBeanTest extends QpidTestCase
+{
+ private static final String QUEUE_NAME = "QUEUE_NAME";
+ private static final String QUEUE_DESCRIPTION = "QUEUE_DESCRIPTION";
+ private static final String QUEUE_TYPE = "QUEUE_TYPE";
+ private static final String QUEUE_ALTERNATE_EXCHANGE = "QUEUE_ALTERNATE_EXCHANGE";
+
+ private Queue _mockQueue;
+ private Statistics _mockQueueStatistics;
+ private VirtualHostMBean _mockVirtualHostMBean;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+ private QueueMBean _queueMBean;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _mockQueue = mock(Queue.class);
+ _mockQueueStatistics = mock(Statistics.class);
+ when(_mockQueue.getName()).thenReturn(QUEUE_NAME);
+ when(_mockQueue.getStatistics()).thenReturn(_mockQueueStatistics);
+ _mockVirtualHostMBean = mock(VirtualHostMBean.class);
+
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+ when(_mockVirtualHostMBean.getRegistry()).thenReturn(_mockManagedObjectRegistry);
+
+ _queueMBean = new QueueMBean(_mockQueue, _mockVirtualHostMBean);
+ }
+
+ public void testQueueName()
+ {
+ assertEquals(QUEUE_NAME, _queueMBean.getName());
+ }
+
+ /********** Statistics **********/
+
+ public void testGetMessageCount() throws Exception
+ {
+ assertStatistic("messageCount", 1000, Queue.QUEUE_DEPTH_MESSAGES);
+ }
+
+ public void testGetReceivedMessageCount() throws Exception
+ {
+ assertStatistic("receivedMessageCount", 1000l, Queue.TOTAL_ENQUEUED_MESSAGES);
+ }
+
+ public void testQueueDepth() throws Exception
+ {
+ assertStatistic("queueDepth", 4096l, Queue.QUEUE_DEPTH_BYTES);
+ }
+
+ public void testActiveConsumerCount() throws Exception
+ {
+ assertStatistic("activeConsumerCount", 3, Queue.CONSUMER_COUNT_WITH_CREDIT);
+ }
+
+ public void testConsumerCount() throws Exception
+ {
+ assertStatistic("consumerCount", 3, Queue.CONSUMER_COUNT);
+ }
+
+ /********** Simple Attributes **********/
+
+ public void testGetQueueDescription() throws Exception
+ {
+ assertAttribute("description", QUEUE_DESCRIPTION, Queue.DESCRIPTION);
+ }
+
+ public void testSetQueueDescription() throws Exception
+ {
+ testSetAttribute("description", Queue.DESCRIPTION, "descriptionold", "descriptionnew");
+ }
+
+ public void testQueueType() throws Exception
+ {
+ assertAttribute("queueType", QUEUE_TYPE, Queue.TYPE);
+ }
+
+ public void testMaximumDeliveryCount() throws Exception
+ {
+ assertAttribute("maximumDeliveryCount", 5, Queue.MAXIMUM_DELIVERY_ATTEMPTS);
+ }
+
+ public void testOwner() throws Exception
+ {
+ assertAttribute("owner", "testOwner", Queue.OWNER);
+ }
+
+ public void testIsDurable() throws Exception
+ {
+ when(_mockQueue.isDurable()).thenReturn(true);
+ assertTrue(_queueMBean.isDurable());
+ }
+
+ public void testIsNotDurable() throws Exception
+ {
+ when(_mockQueue.isDurable()).thenReturn(false);
+ assertFalse(_queueMBean.isDurable());
+ }
+
+ public void testIsAutoDelete() throws Exception
+ {
+ when(_mockQueue.getLifetimePolicy()).thenReturn(LifetimePolicy.AUTO_DELETE);
+ assertTrue(_queueMBean.isAutoDelete());
+ }
+
+ public void testIsNotAutoDelete() throws Exception
+ {
+ when(_mockQueue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT);
+ assertFalse(_queueMBean.isAutoDelete());
+ }
+
+ public void testGetMaximumMessageAge() throws Exception
+ {
+ assertAttribute("maximumMessageAge", 10000l, Queue.ALERT_THRESHOLD_MESSAGE_AGE);
+ }
+
+ public void testSetMaximumMessageAge() throws Exception
+ {
+ testSetAttribute("maximumMessageAge", Queue.ALERT_THRESHOLD_MESSAGE_AGE, 1000l, 10000l);
+ }
+
+ public void testGetMaximumMessageSize() throws Exception
+ {
+ assertAttribute("maximumMessageSize", 1024l, Queue.ALERT_THRESHOLD_MESSAGE_SIZE);
+ }
+
+ public void testSetMaximumMessageSize() throws Exception
+ {
+ testSetAttribute("maximumMessageSize", Queue.ALERT_THRESHOLD_MESSAGE_SIZE, 1024l, 2048l);
+ }
+
+ public void testGetMaximumMessageCount() throws Exception
+ {
+ assertAttribute("maximumMessageCount", 5000l, Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES);
+ }
+
+ public void testSetMaximumMessageCount() throws Exception
+ {
+ testSetAttribute("maximumMessageCount", Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 4000l, 5000l);
+ }
+
+ public void testGetMaximumQueueDepth() throws Exception
+ {
+ assertAttribute("maximumQueueDepth", 1048576l, Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES);
+ }
+
+ public void testSetMaximumQueueDepth() throws Exception
+ {
+ testSetAttribute("maximumQueueDepth", Queue.ALERT_THRESHOLD_QUEUE_DEPTH_BYTES,1048576l , 2097152l);
+ }
+
+ public void testGetCapacity() throws Exception
+ {
+ assertAttribute("capacity", 1048576l, Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES);
+ }
+
+ public void testSetCapacity() throws Exception
+ {
+ testSetAttribute("capacity", Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES,1048576l , 2097152l);
+ }
+
+ public void testGetFlowResumeCapacity() throws Exception
+ {
+ assertAttribute("flowResumeCapacity", 1048576l, Queue.QUEUE_FLOW_RESUME_SIZE_BYTES);
+ }
+
+ public void testSetFlowResumeCapacity() throws Exception
+ {
+ testSetAttribute("flowResumeCapacity", Queue.QUEUE_FLOW_RESUME_SIZE_BYTES,1048576l , 2097152l);
+ }
+
+ public void testIsExclusive() throws Exception
+ {
+ assertAttribute("exclusive", Boolean.TRUE, Queue.EXCLUSIVE);
+ }
+
+ public void testIsNotExclusive() throws Exception
+ {
+ assertAttribute("exclusive", Boolean.FALSE, Queue.EXCLUSIVE);
+ }
+
+ public void testSetExclusive() throws Exception
+ {
+ testSetAttribute("exclusive", Queue.EXCLUSIVE, Boolean.FALSE , Boolean.TRUE);
+ }
+
+ /********** Other attributes **********/
+
+ public void testGetAlternateExchange()
+ {
+ Exchange mockAlternateExchange = mock(Exchange.class);
+ when(mockAlternateExchange.getName()).thenReturn(QUEUE_ALTERNATE_EXCHANGE);
+
+ when(_mockQueue.getAttribute(Queue.ALTERNATE_EXCHANGE)).thenReturn(mockAlternateExchange);
+
+ assertEquals(QUEUE_ALTERNATE_EXCHANGE, _queueMBean.getAlternateExchange());
+ }
+
+ public void testGetAlternateExchangeWhenQueueHasNone()
+ {
+ when(_mockQueue.getAttribute(Queue.ALTERNATE_EXCHANGE)).thenReturn(null);
+
+ assertNull(_queueMBean.getAlternateExchange());
+ }
+
+ public void testSetAlternateExchange() throws Exception
+ {
+ Exchange mockExchange1 = mock(Exchange.class);
+ when(mockExchange1.getName()).thenReturn("exchange1");
+
+ Exchange mockExchange2 = mock(Exchange.class);
+ when(mockExchange2.getName()).thenReturn("exchange2");
+
+ Exchange mockExchange3 = mock(Exchange.class);
+ when(mockExchange3.getName()).thenReturn("exchange3");
+
+ VirtualHost mockVirtualHost = mock(VirtualHost.class);
+ when(mockVirtualHost.getExchanges()).thenReturn(Arrays.asList(new Exchange[] {mockExchange1, mockExchange2, mockExchange3}));
+ when(_mockQueue.getParent(VirtualHost.class)).thenReturn(mockVirtualHost);
+
+ _queueMBean.setAlternateExchange("exchange2");
+ verify(_mockQueue).setAttribute(Queue.ALTERNATE_EXCHANGE, null, mockExchange2);
+ }
+
+ public void testSetAlternateExchangeWithUnknownExchangeName() throws Exception
+ {
+ Exchange mockExchange = mock(Exchange.class);
+ when(mockExchange.getName()).thenReturn("exchange1");
+
+ VirtualHost mockVirtualHost = mock(VirtualHost.class);
+ when(mockVirtualHost.getExchanges()).thenReturn(Collections.singletonList(mockExchange));
+ when(_mockQueue.getParent(VirtualHost.class)).thenReturn(mockVirtualHost);
+
+ try
+ {
+ _queueMBean.setAlternateExchange("notknown");
+ fail("Exception not thrown");
+ }
+ catch(OperationsException oe)
+ {
+ // PASS
+ }
+ }
+
+ public void testRemoveAlternateExchange() throws Exception
+ {
+ _queueMBean.setAlternateExchange("");
+ verify(_mockQueue).setAttribute(Queue.ALTERNATE_EXCHANGE, null, null);
+ }
+
+ /********** Operations **********/
+
+ /********** Notifications **********/
+
+ public void testNotificationListenerCalled() throws Exception
+ {
+ NotificationListener listener = mock(NotificationListener.class);
+ _queueMBean.addNotificationListener(listener, null, null);
+
+ NotificationCheck notification = mock(NotificationCheck.class);
+ String notificationMsg = "Test notification message";
+
+ _queueMBean.notifyClients(notification, _mockQueue, notificationMsg);
+ verify(listener).handleNotification(isNotificationWithMessage(notificationMsg),
+ isNull());
+ }
+
+ public void testAddRemoveNotificationListener() throws Exception
+ {
+ NotificationListener listener1 = mock(NotificationListener.class);
+ _queueMBean.addNotificationListener(listener1, null, null);
+ _queueMBean.removeNotificationListener(listener1);
+ }
+
+ public void testRemoveUnknownNotificationListener() throws Exception
+ {
+ NotificationListener listener1 = mock(NotificationListener.class);
+ try
+ {
+ _queueMBean.removeNotificationListener(listener1);
+ fail("Exception not thrown");
+ }
+ catch (ListenerNotFoundException e)
+ {
+ // PASS
+ }
+ }
+
+ private Notification isNotificationWithMessage(final String expectedMessage)
+ {
+ return argThat( new ArgumentMatcher<Notification>()
+ {
+ @Override
+ public boolean matches(Object argument)
+ {
+ Notification actual = (Notification) argument;
+ return actual.getMessage().endsWith(expectedMessage);
+ }
+ });
+ }
+
+ private void assertStatistic(String jmxAttributeName, Object expectedValue, String underlyingAttributeName) throws Exception
+ {
+ when(_mockQueueStatistics.getStatistic(underlyingAttributeName)).thenReturn(expectedValue);
+ MBeanTestUtils.assertMBeanAttribute(_queueMBean, jmxAttributeName, expectedValue);
+ }
+
+ private void assertAttribute(String jmxAttributeName, Object expectedValue, String underlyingAttributeName) throws Exception
+ {
+ when(_mockQueue.getAttribute(underlyingAttributeName)).thenReturn(expectedValue);
+ MBeanTestUtils.assertMBeanAttribute(_queueMBean, jmxAttributeName, expectedValue);
+ }
+
+ private void testSetAttribute(String jmxAttributeName, String underlyingAttributeName, Object originalAttributeValue, Object newAttributeValue) throws Exception
+ {
+ when(_mockQueue.getAttribute(underlyingAttributeName)).thenReturn(originalAttributeValue);
+
+ MBeanTestUtils.setMBeanAttribute(_queueMBean, jmxAttributeName, newAttributeValue);
+
+ verify(_mockQueue).setAttribute(underlyingAttributeName, originalAttributeValue, newAttributeValue);
+ }
+
+ public void testViewMessageContent() throws Exception
+ {
+ viewMessageContentTestImpl(16L, 1000, 1000);
+ }
+
+ public void testViewMessageContentWithMissingPayload() throws Exception
+ {
+ viewMessageContentTestImpl(16L, 1000, 0);
+ }
+
+ private void viewMessageContentTestImpl(final long messageNumber,
+ final int messageSize,
+ final int messageContentSize) throws Exception
+ {
+ final byte[] content = new byte[messageContentSize];
+
+ //mock message and queue entry to return a given message size, and have a given content
+ final ServerMessage<?> serverMessage = mock(ServerMessage.class);
+ when(serverMessage.getMessageNumber()).thenReturn(messageNumber);
+ when(serverMessage.getSize()).thenReturn((long)messageSize);
+ doAnswer(new Answer<Object>()
+ {
+ public Object answer(InvocationOnMock invocation)
+ {
+ Object[] args = invocation.getArguments();
+
+ //verify the arg types / expected values
+ assertEquals(2, args.length);
+ assertTrue(args[0] instanceof ByteBuffer);
+ assertTrue(args[1] instanceof Integer);
+
+ ByteBuffer dest = (ByteBuffer) args[0];
+ int offset = (Integer) args[1];
+ assertEquals(0, offset);
+
+ dest.put(content);
+ return messageContentSize;
+ }
+ }).when(serverMessage).getContent(Matchers.any(ByteBuffer.class), Matchers.anyInt());
+
+ final QueueEntry entry = mock(QueueEntry.class);
+ when(entry.getMessage()).thenReturn(serverMessage);
+
+ //mock the queue.visit() method to ensure we match the mock message
+ doAnswer(new Answer<Object>()
+ {
+ public Object answer(InvocationOnMock invocation)
+ {
+ Object[] args = invocation.getArguments();
+ GetMessageVisitor visitor = (GetMessageVisitor) args[0];
+ visitor.visit(entry);
+ return null;
+ }
+ }).when(_mockQueue).visit(Matchers.any(GetMessageVisitor.class));
+
+ //now retrieve the content and verify its size
+ CompositeDataSupport comp = (CompositeDataSupport) _queueMBean.viewMessageContent(messageNumber);
+ assertNotNull(comp);
+ byte[] data = (byte[]) comp.get(ManagedQueue.CONTENT);
+ assertEquals(messageSize, data.length);
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBeanTest.java
new file mode 100644
index 0000000000..86eab0245e
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBeanTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Statistics;
+
+import junit.framework.TestCase;
+
+public class ServerInformationMBeanTest extends TestCase
+{
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+ private Broker _mockBroker;
+ private Statistics _mockBrokerStatistics;
+ private ServerInformationMBean _mbean;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+ _mockBroker = mock(Broker.class);
+ _mockBrokerStatistics = mock(Statistics.class);
+ when(_mockBroker.getStatistics()).thenReturn(_mockBrokerStatistics);
+
+ _mbean = new ServerInformationMBean(_mockManagedObjectRegistry, _mockBroker);
+ }
+
+ public void testMBeanRegistersItself() throws Exception
+ {
+ ServerInformationMBean mbean = new ServerInformationMBean(_mockManagedObjectRegistry, _mockBroker);
+ verify(_mockManagedObjectRegistry).registerObject(mbean);
+ }
+
+ /********** Statistics **********/
+
+ public void testGetMessageCount() throws Exception
+ {
+ assertStatistic("totalDataDelivered", 16384l, Connection.BYTES_OUT);
+ }
+
+ /********** Attributes **********/
+
+ public void testBuildVersion() throws Exception
+ {
+ assertAttribute("buildVersion", "0.0.1", Broker.BUILD_VERSION);
+ }
+
+ public void testProductVersion() throws Exception
+ {
+ assertAttribute("productVersion", "0.0.1", Broker.PRODUCT_VERSION);
+ }
+
+ /********** Other Attributes **********/
+
+ public void testIsStatisticsEnabled() throws Exception
+ {
+ assertTrue("isStatisticsEnabled", _mbean.isStatisticsEnabled());
+ }
+
+ private void assertStatistic(String jmxAttributeName, Object expectedValue, String underlyingAttributeName) throws Exception
+ {
+ when(_mockBrokerStatistics.getStatistic(underlyingAttributeName)).thenReturn(expectedValue);
+ MBeanTestUtils.assertMBeanAttribute(_mbean, jmxAttributeName, expectedValue);
+ }
+
+ private void assertAttribute(String jmxAttributeName, Object expectedValue, String underlyingAttributeName) throws Exception
+ {
+ when(_mockBroker.getAttribute(underlyingAttributeName)).thenReturn(expectedValue);
+ MBeanTestUtils.assertMBeanAttribute(_mbean, jmxAttributeName, expectedValue);
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java
new file mode 100644
index 0000000000..8ca6c521eb
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+import javax.security.auth.login.AccountNotFoundException;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.management.common.mbeans.UserManagement;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
+
+public class UserManagementMBeanTest extends TestCase
+{
+ private UserManagementMBean _userManagement;
+ private ManagedObjectRegistry _mockRegistry;
+ private PasswordCredentialManagingAuthenticationProvider _mockProvider;
+
+ private static final String TEST_USERNAME = "testuser";
+ private static final String TEST_PASSWORD = "password";
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ _mockProvider = mock(PasswordCredentialManagingAuthenticationProvider.class);
+ _mockRegistry = mock(ManagedObjectRegistry.class);
+ _userManagement = new UserManagementMBean(_mockProvider, _mockRegistry);
+ }
+
+ public void testMBeanRegistersItself() throws Exception
+ {
+ UserManagementMBean userManagementMBean = new UserManagementMBean(_mockProvider, _mockRegistry);
+ verify(_mockRegistry).registerObject(userManagementMBean);
+ }
+
+ public void testDeleteUser() throws Exception
+ {
+ boolean deleteSuccess = _userManagement.deleteUser(TEST_USERNAME);
+ assertTrue("Expected successful delete", deleteSuccess);
+
+ verify(_mockProvider).deleteUser(TEST_USERNAME);
+ }
+
+ public void testDeleteUserWhereUserDoesNotExist() throws Exception
+ {
+ doThrow(AccountNotFoundException.class).when(_mockProvider).deleteUser(TEST_USERNAME);
+
+ boolean deleteSuccess = _userManagement.deleteUser(TEST_USERNAME);
+ assertFalse("Expected unsuccessful delete", deleteSuccess);
+ }
+
+ public void testCreateUser() throws Exception
+ {
+ when(_mockProvider.createUser(TEST_USERNAME, TEST_PASSWORD, null)).thenReturn(true);
+
+ boolean createSuccess = _userManagement.createUser(TEST_USERNAME, TEST_PASSWORD);
+ assertTrue(createSuccess);
+ }
+
+ public void testCreateUserWhereUserAlreadyExists()
+ {
+ when(_mockProvider.createUser(TEST_USERNAME, TEST_PASSWORD, null)).thenReturn(false);
+
+ boolean createSuccess = _userManagement.createUser(TEST_USERNAME, TEST_PASSWORD);
+ assertFalse(createSuccess);
+ }
+
+ public void testSetPassword() throws Exception
+ {
+ boolean setPasswordSuccess = _userManagement.setPassword(TEST_USERNAME, TEST_PASSWORD);
+ assertTrue(setPasswordSuccess);
+
+ assertTrue("Set password should return true to flag successful change", setPasswordSuccess);
+
+ verify(_mockProvider).setPassword(TEST_USERNAME, TEST_PASSWORD);
+ }
+
+ public void testSetPasswordWhereUserDoesNotExist() throws Exception
+ {
+ doThrow(AccountNotFoundException.class).when(_mockProvider).setPassword(TEST_USERNAME, TEST_PASSWORD);
+
+ boolean setPasswordSuccess = _userManagement.setPassword(TEST_USERNAME, TEST_PASSWORD);
+
+ assertFalse("Set password should return false to flag unsuccessful change", setPasswordSuccess);
+ }
+
+ public void testReload() throws Exception
+ {
+ boolean reloadSuccess = _userManagement.reloadData();
+
+ assertTrue("Reload should return true to flag succesful update", reloadSuccess);
+
+ verify(_mockProvider).reload();
+ }
+
+ public void testReloadFails() throws Exception
+ {
+ doThrow(IOException.class).when(_mockProvider).reload();
+
+ boolean reloadSuccess = _userManagement.reloadData();
+
+ assertFalse("Expected reload to fail", reloadSuccess);
+ }
+
+ public void testViewUsers() throws Exception
+ {
+ Map<String,String> args = Collections.emptyMap();
+ when(_mockProvider.getUsers()).thenReturn(Collections.singletonMap(TEST_USERNAME, args));
+
+ TabularData userList = _userManagement.viewUsers();
+
+ assertNotNull(userList);
+ assertEquals("Unexpected number of users in user list", 1, userList.size());
+ assertTrue(userList.containsKey(new Object[]{TEST_USERNAME}));
+
+ // Check the deprecated read, write and admin items continue to exist but return false.
+ CompositeData userRec = userList.get(new Object[]{TEST_USERNAME});
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_ONLY));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_ONLY));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_WRITE));
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_WRITE));
+ assertTrue(userRec.containsKey(UserManagement.RIGHTS_ADMIN));
+ assertEquals(false, userRec.get(UserManagement.RIGHTS_ADMIN));
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java
new file mode 100644
index 0000000000..e3fac9f711
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.jmx.mbeans;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.management.OperationsException;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+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.State;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+
+public class VirtualHostManagerMBeanTest extends TestCase
+{
+ private static final String TEST_QUEUE_NAME = "QUEUE_NAME";
+ private static final String TEST_EXCHANGE_NAME = "EXCHANGE_NAME";
+ private static final String TEST_OWNER = "OWNER";
+ private static final String TEST_DESCRIPTION = "DESCRIPTION";
+ private static final String TEST_EXCHANGE_TYPE = "EXCHANGE_TYPE";
+
+ private static final Map<String, Object> EMPTY_ARGUMENT_MAP = Collections.emptyMap();
+
+ private VirtualHost _mockVirtualHost;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
+ private VirtualHostManagerMBean _virtualHostManagerMBean;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockVirtualHost = mock(VirtualHost.class);
+ when(_mockVirtualHost.getExchangeTypes()).thenReturn(Collections.singletonList(TEST_EXCHANGE_TYPE));
+
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
+
+ _virtualHostManagerMBean = new VirtualHostManagerMBean(new VirtualHostMBean(_mockVirtualHost, _mockManagedObjectRegistry));
+ }
+
+ public void testCreateQueueWithNoOwner() throws Exception
+ {
+ _virtualHostManagerMBean.createNewQueue(TEST_QUEUE_NAME, null, true);
+
+ verify(_mockVirtualHost).createQueue(TEST_QUEUE_NAME, State.ACTIVE, true, false, LifetimePolicy.PERMANENT, 0, EMPTY_ARGUMENT_MAP);
+ }
+
+ /**
+ * Some users have been abusing the owner parameter as a description. Decision has been taken to map this parameter
+ * through to the description field (if the description field is passed, the owner is discarded).
+ */
+ public void testCreateQueueWithOwnerMappedThroughToDescription() throws Exception
+ {
+ _virtualHostManagerMBean.createNewQueue(TEST_QUEUE_NAME, TEST_OWNER, true);
+
+ Map<String, Object> expectedArguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_OWNER);
+ verify(_mockVirtualHost).createQueue(TEST_QUEUE_NAME, State.ACTIVE, true, false, LifetimePolicy.PERMANENT, 0, expectedArguments);
+ }
+
+ public void testCreateQueueWithOwnerAndDescriptionDiscardsOwner() throws Exception
+ {
+ Map<String, Object> arguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_DESCRIPTION);
+ _virtualHostManagerMBean.createNewQueue(TEST_QUEUE_NAME, TEST_OWNER, true, arguments);
+
+ Map<String, Object> expectedArguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_DESCRIPTION);
+ verify(_mockVirtualHost).createQueue(TEST_QUEUE_NAME, State.ACTIVE, true, false, LifetimePolicy.PERMANENT, 0, expectedArguments);
+ }
+
+ public void testDeleteQueue() throws Exception
+ {
+ Queue mockQueue = mock(Queue.class);
+ when(mockQueue.getName()).thenReturn("queue1");
+ when(_mockVirtualHost.getQueues()).thenReturn(Collections.singletonList(mockQueue));
+
+ _virtualHostManagerMBean.deleteQueue("queue1");
+ verify(mockQueue).delete();
+ }
+
+ public void testDeleteQueueWhenQueueDoesNotExist() throws Exception
+ {
+ Queue mockQueue = mock(Queue.class);
+ when(mockQueue.getName()).thenReturn("queue1");
+ when(_mockVirtualHost.getQueues()).thenReturn(Collections.singletonList(mockQueue));
+
+ try
+ {
+ _virtualHostManagerMBean.deleteQueue("unknownqueue");
+ fail("Exception not thrown");
+ }
+ catch(OperationsException oe)
+ {
+ // PASS
+ assertEquals("No such queue \"unknownqueue\"", oe.getMessage());
+ }
+ verify(mockQueue, never()).delete();
+ }
+
+ public void testCreateNewDurableExchange() throws Exception
+ {
+ _virtualHostManagerMBean.createNewExchange(TEST_EXCHANGE_NAME, TEST_EXCHANGE_TYPE, true);
+ verify(_mockVirtualHost).createExchange(TEST_EXCHANGE_NAME, State.ACTIVE, true, LifetimePolicy.PERMANENT, 0, TEST_EXCHANGE_TYPE, EMPTY_ARGUMENT_MAP);
+ }
+
+ public void testCreateNewExchangeWithUnknownExchangeType() throws Exception
+ {
+ String exchangeType = "notknown";
+ try
+ {
+ _virtualHostManagerMBean.createNewExchange(TEST_EXCHANGE_NAME, exchangeType, true);
+ fail("Exception not thrown");
+ }
+ catch (OperationsException oe)
+ {
+ // PASS
+ }
+ verify(_mockVirtualHost, never()).createExchange(TEST_EXCHANGE_NAME, State.ACTIVE, true, LifetimePolicy.PERMANENT, 0, exchangeType, EMPTY_ARGUMENT_MAP);
+ }
+
+ public void testUnregisterExchange() throws Exception
+ {
+ Exchange mockExchange = mock(Exchange.class);
+ when(mockExchange.getName()).thenReturn("exchange1");
+ when(_mockVirtualHost.getExchanges()).thenReturn(Collections.singletonList(mockExchange));
+
+ _virtualHostManagerMBean.unregisterExchange("exchange1");
+ verify(mockExchange).delete();
+ }
+
+ public void testUnregisterExchangeWhenExchangeDoesNotExist() throws Exception
+ {
+ Exchange mockExchange = mock(Exchange.class);
+ when(mockExchange.getName()).thenReturn("exchange1");
+ when(_mockVirtualHost.getExchanges()).thenReturn(Collections.singletonList(mockExchange));
+
+ try
+ {
+ _virtualHostManagerMBean.unregisterExchange("unknownexchange");
+ fail("Exception not thrown");
+ }
+ catch(OperationsException oe)
+ {
+ // PASS
+ assertEquals("No such exchange \"unknownexchange\"", oe.getMessage());
+ }
+
+ verify(mockExchange, never()).delete();
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java
new file mode 100644
index 0000000000..7473a4d3e7
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.systest.management.jmx;
+
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+/**
+ * Tests the JMX API for the Managed Broker.
+ *
+ */
+public class BrokerManagementTest extends QpidBrokerTestCase
+{
+ private static final String VIRTUAL_HOST = "test";
+
+ /**
+ * JMX helper.
+ */
+ private JMXTestUtils _jmxUtils;
+ private ManagedBroker _managedBroker;
+
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+ super.setUp();
+ _jmxUtils.open();
+ _managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST);
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Tests queue creation/deletion also verifying the automatic binding to the default exchange.
+ */
+ public void testCreateQueueAndDeletion() throws Exception
+ {
+ final String queueName = getTestQueueName();
+ final ManagedExchange defaultExchange = _jmxUtils.getManagedExchange(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString());
+
+ // Check that bind does not exist before queue creation
+ assertFalse("Binding to " + queueName + " should not exist in default exchange before queue creation",
+ defaultExchange.bindings().containsKey(new String[] {queueName}));
+
+ _managedBroker.createNewQueue(queueName, "testowner", true);
+
+ // Ensure the queue exists
+ assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName));
+ assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName));
+
+ // Now verify that the default exchange has been bound.
+ assertTrue("Binding to " + queueName + " should exist in default exchange after queue creation",
+ defaultExchange.bindings().containsKey(new String[] {queueName}));
+
+ // Now delete the queue
+ _managedBroker.deleteQueue(queueName);
+
+ // Finally ensure that the binding has been removed.
+ assertFalse("Binding to " + queueName + " should not exist in default exchange after queue deletion",
+ defaultExchange.bindings().containsKey(new String[] {queueName}));
+ }
+
+ /**
+ * Tests exchange creation/deletion via JMX API.
+ */
+ public void testCreateExchangeAndUnregister() throws Exception
+ {
+ String exchangeName = getTestName();
+ _managedBroker.createNewExchange(exchangeName, "topic", true);
+
+ ManagedExchange exchange = _jmxUtils.getManagedExchange(exchangeName);
+ assertNotNull("Exchange should exist", exchange);
+
+ _managedBroker.unregisterExchange(exchangeName);
+ }
+
+ /**
+ * Tests that it is disallowed to unregister the default exchange.
+ */
+ public void testUnregisterOfDefaultExchangeDisallowed() throws Exception
+ {
+ String defaultExchangeName = ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString();
+
+ try
+ {
+ _managedBroker.unregisterExchange(defaultExchangeName);
+ fail("Exception not thrown");
+ }
+ catch (UnsupportedOperationException e)
+ {
+ // PASS
+ assertEquals("'<<default>>' is a reserved exchange and can't be deleted", e.getMessage());
+ }
+ final ManagedExchange defaultExchange = _jmxUtils.getManagedExchange(defaultExchangeName);
+ assertNotNull("Exchange should exist", defaultExchange);
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java
new file mode 100644
index 0000000000..28d7bf4aed
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java
@@ -0,0 +1,283 @@
+/*
+ * 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.systest.management.jmx;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.TabularData;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.qpid.common.QpidProperties;
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class ConnectionManagementTest extends QpidBrokerTestCase
+{
+ private static final String VIRTUAL_HOST_NAME = "test";
+
+ private JMXTestUtils _jmxUtils;
+ private Connection _connection;
+
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp(); // modifies broker config therefore must be done before super.setUp()
+ super.setUp();
+ _jmxUtils.open();
+ }
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testNumberOfManagedConnectionsMatchesNumberOfClientConnections() throws Exception
+ {
+ assertEquals("Expected no managed connections", 0, getManagedConnections().size());
+
+ _connection = getConnection();
+ assertEquals("Expected one managed connection", 1, getManagedConnections().size());
+
+ _connection.close();
+ assertEquals("Expected no managed connections after client connection closed", 0, getManagedConnections().size());
+ }
+
+ public void testGetAttributes() throws Exception
+ {
+ _connection = getConnection();
+ final ManagedConnection mBean = getConnectionMBean();
+
+ checkAuthorisedId(mBean);
+ checkClientVersion(mBean);
+ checkClientId(mBean);
+ }
+
+ public void testNonTransactedSession() throws Exception
+ {
+ _connection = getConnection();
+
+ boolean transactional = false;
+ boolean flowBlocked = false;
+
+ _connection.createSession(transactional, Session.AUTO_ACKNOWLEDGE);
+
+ final ManagedConnection mBean = getConnectionMBean();
+ final CompositeDataSupport row = getTheOneChannelRow(mBean);
+ assertChannelRowData(row, 0, transactional, flowBlocked);
+ }
+
+ public void testTransactedSessionWithUnackMessages() throws Exception
+ {
+ _connection = getConnection();
+ _connection.start();
+
+ boolean transactional = true;
+ int numberOfMessages = 2;
+ final Session session = _connection.createSession(transactional, Session.SESSION_TRANSACTED);
+ final Destination destination = session.createQueue(getTestQueueName());
+ final MessageConsumer consumer = session.createConsumer(destination);
+
+ sendMessage(session, destination, numberOfMessages);
+ receiveMessagesWithoutCommit(consumer, numberOfMessages);
+
+ final ManagedConnection mBean = getConnectionMBean();
+ final CompositeDataSupport row = getTheOneChannelRow(mBean);
+ boolean flowBlocked = false;
+ assertChannelRowData(row, numberOfMessages, transactional, flowBlocked);
+
+ // check that commit advances the lastIoTime
+ final Date initialLastIOTime = mBean.getLastIoTime();
+ session.commit();
+ assertTrue("commit should have caused last IO time to advance", mBean.getLastIoTime().after(initialLastIOTime));
+
+ // check that channels() now returns one session with no unacknowledged messages
+ final CompositeDataSupport rowAfterCommit = getTheOneChannelRow(mBean);
+ final Number unackCountAfterCommit = (Number) rowAfterCommit.get(ManagedConnection.UNACKED_COUNT);
+ assertEquals("Unexpected number of unacknowledged messages", 0, unackCountAfterCommit);
+ }
+
+
+ public void testProducerFlowBlocked() throws Exception
+ {
+ _connection = getConnection();
+ _connection.start();
+
+ String queueName = getTestQueueName();
+ Session session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ Queue queue = session.createQueue(queueName);
+ createQueueOnBroker(session, queue);
+
+ ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ managedQueue.setFlowResumeCapacity(DEFAULT_MESSAGE_SIZE * 2l);
+ managedQueue.setCapacity(DEFAULT_MESSAGE_SIZE * 3l);
+
+ final ManagedConnection managedConnection = getConnectionMBean();
+
+ // Check that producer flow is not block before test
+ final CompositeDataSupport rowBeforeSend = getTheOneChannelRow(managedConnection);
+ assertFlowBlocked(rowBeforeSend, false);
+
+
+ // Check that producer flow does not become block too soon
+ sendMessage(session, queue, 3);
+ final CompositeDataSupport rowBeforeFull = getTheOneChannelRow(managedConnection);
+ assertFlowBlocked(rowBeforeFull, false);
+
+ // Fourth message will over-fill the queue (but as we are not sending more messages, client thread wont't block)
+ sendMessage(session, queue, 1);
+ final CompositeDataSupport rowAfterFull = getTheOneChannelRow(managedConnection);
+ assertFlowBlocked(rowAfterFull, true);
+
+ // Consume two to bring the queue down to the resume capacity
+ MessageConsumer consumer = session.createConsumer(queue);
+ assertNotNull("Could not receive first message", consumer.receive(1000));
+ assertNotNull("Could not receive second message", consumer.receive(1000));
+ session.commit();
+
+ // Check that producer flow is no longer blocked
+ final CompositeDataSupport rowAfterReceive = getTheOneChannelRow(managedConnection);
+ assertFlowBlocked(rowAfterReceive, false);
+ }
+
+ private void createQueueOnBroker(Session session, Destination destination) throws JMSException
+ {
+ session.createConsumer(destination).close(); // Create a consumer only to cause queue creation
+ }
+
+ private void assertChannelRowData(final CompositeData row, int unacknowledgedMessages, boolean isTransactional, boolean flowBlocked)
+ {
+ assertNotNull(row);
+ assertEquals("Unexpected transactional flag", isTransactional, row.get(ManagedConnection.TRANSACTIONAL));
+ assertEquals("Unexpected unacknowledged message count", unacknowledgedMessages, row.get(ManagedConnection.UNACKED_COUNT));
+ assertEquals("Unexpected flow blocked", flowBlocked, row.get(ManagedConnection.FLOW_BLOCKED));
+ }
+
+ private void assertFlowBlocked(final CompositeData row, boolean flowBlocked)
+ {
+ assertNotNull(row);
+ assertEquals("Unexpected flow blocked", flowBlocked, row.get(ManagedConnection.FLOW_BLOCKED));
+ }
+
+ private void checkAuthorisedId(ManagedConnection mBean) throws Exception
+ {
+ assertEquals("Unexpected authorized id", GUEST_USERNAME, mBean.getAuthorizedId());
+ }
+
+ private void checkClientVersion(ManagedConnection mBean) throws Exception
+ {
+ String expectedVersion = QpidProperties.getReleaseVersion();
+ assertTrue(StringUtils.isNotBlank(expectedVersion));
+
+ assertEquals("Unexpected version", expectedVersion, mBean.getVersion());
+ }
+
+ private void checkClientId(ManagedConnection mBean) throws Exception
+ {
+ String expectedClientId = _connection.getClientID();
+ assertTrue(StringUtils.isNotBlank(expectedClientId));
+
+ assertEquals("Unexpected ClientId", expectedClientId, mBean.getClientId());
+ }
+
+ private ManagedConnection getConnectionMBean()
+ {
+ List<ManagedConnection> connections = getManagedConnections();
+ assertNotNull("Connection MBean is not found", connections);
+ assertEquals("Unexpected number of connection mbeans", 1, connections.size());
+ final ManagedConnection mBean = connections.get(0);
+ assertNotNull("Connection MBean is null", mBean);
+ return mBean;
+ }
+
+ private List<ManagedConnection> getManagedConnections()
+ {
+ return _jmxUtils.getManagedConnections(VIRTUAL_HOST_NAME);
+ }
+
+ private CompositeDataSupport getTheOneChannelRow(final ManagedConnection mBean) throws Exception
+ {
+ TabularData channelsData = getChannelsDataWithRetry(mBean);
+
+ assertEquals("Unexpected number of rows in channel table", 1, channelsData.size());
+
+ @SuppressWarnings("unchecked")
+ final Iterator<CompositeDataSupport> rowItr = (Iterator<CompositeDataSupport>) channelsData.values().iterator();
+ final CompositeDataSupport row = rowItr.next();
+ return row;
+ }
+
+ private void receiveMessagesWithoutCommit(final MessageConsumer consumer, int numberOfMessages) throws Exception
+ {
+ for (int i = 0; i < numberOfMessages; i++)
+ {
+ final Message m = consumer.receive(1000l);
+ assertNotNull("Message " + i + " is not received", m);
+ }
+ }
+
+ private TabularData getChannelsDataWithRetry(final ManagedConnection mBean)
+ throws IOException, JMException
+ {
+ TabularData channelsData = mBean.channels();
+ int retries = 0;
+ while(channelsData.size() == 0 && retries < 5)
+ {
+ sleep();
+ channelsData = mBean.channels();
+ retries++;
+ }
+ return channelsData;
+ }
+
+ private void sleep()
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java
new file mode 100644
index 0000000000..ac6730638e
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.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.systest.management.jmx;
+
+import java.io.File;
+import java.util.List;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBeanTest;
+import org.apache.qpid.server.logging.log4j.LoggingFacadeTest;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.util.FileUtils;
+import org.apache.qpid.util.LogMonitor;
+
+/**
+ * System test for Logging Management. <b>These tests rely on value set within
+ * test-profiles/log4j-test.xml</b>.
+ *
+ * @see LoggingManagementMBeanTest
+ * @see LoggingFacadeTest
+ *
+ */
+public class LoggingManagementTest extends QpidBrokerTestCase
+{
+ private JMXTestUtils _jmxUtils;
+ private LoggingManagement _loggingManagement;
+ private LogMonitor _monitor;
+
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+
+ // System test normally run with log for4j test config from beneath test-profiles. We need to
+ // copy it as some of our tests write to this file.
+
+ File tmpLogFile = File.createTempFile("log4j" + "." + getName(), ".xml");
+ tmpLogFile.deleteOnExit();
+ FileUtils.copy(_logConfigFile, tmpLogFile);
+
+ _logConfigFile = tmpLogFile;
+
+ super.setUp();
+ _jmxUtils.open();
+
+ _loggingManagement = _jmxUtils.getLoggingManagement();
+ _monitor = new LogMonitor(_outputFile);
+ }
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testViewEffectiveRuntimeLoggerLevels() throws Exception
+ {
+ final String qpidMainLogger = "org.apache.qpid";
+
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row = table.get(new String[] {qpidMainLogger} );
+ assertChannelRow(row, qpidMainLogger, "DEBUG");
+ }
+
+ public void testViewConfigFileLoggerLevels() throws Exception
+ {
+ final String operationalLoggingLogger = "qpid.message";
+
+ TabularData table = _loggingManagement.viewConfigFileLoggerLevels();
+ final CompositeData row = table.get(new String[] {operationalLoggingLogger} );
+ assertChannelRow(row, operationalLoggingLogger, "INFO");
+ }
+
+ public void testTurnOffOrgApacheQpidAtRuntime() throws Exception
+ {
+ final String logger = "org.apache.qpid";
+ _monitor.markDiscardPoint();
+ _loggingManagement.setRuntimeLoggerLevel(logger, "OFF");
+
+ List<String> matches = _monitor.findMatches("Setting level to OFF for logger 'org.apache.qpid'");
+ assertEquals(1, matches.size());
+
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row1 = table.get(new String[] {logger} );
+ assertChannelRow(row1, logger, "OFF");
+ }
+
+ public void testChangesToConfigFileBecomeEffectiveAfterReload() throws Exception
+ {
+ final String operationalLoggingLogger = "qpid.message";
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO");
+
+ _monitor.markDiscardPoint();
+ _loggingManagement.setConfigFileLoggerLevel(operationalLoggingLogger, "OFF");
+
+ List<String> matches = _monitor.findMatches("Setting level to OFF for logger 'qpid.message'");
+ assertEquals(1, matches.size());
+
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO");
+
+ _loggingManagement.reloadConfigFile();
+
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "OFF");
+ }
+
+ private void assertEffectiveLoggingLevel(String operationalLoggingLogger, String expectedLevel)
+ {
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row1 = table.get(new String[] {operationalLoggingLogger} );
+ assertChannelRow(row1, operationalLoggingLogger, expectedLevel);
+ }
+
+ private void assertChannelRow(final CompositeData row, String logger, String level)
+ {
+ assertNotNull("No row for " + logger, row);
+ assertEquals("Unexpected logger name", logger, row.get(LoggingManagement.LOGGER_NAME));
+ assertEquals("Unexpected level", level, row.get(LoggingManagement.LOGGER_LEVEL));
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java
new file mode 100644
index 0000000000..47b38381c5
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java
@@ -0,0 +1,480 @@
+/*
+ *
+ * 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.systest.management.jmx;
+
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.server.logging.AbstractTestLogging;
+import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject;
+import org.apache.qpid.test.utils.JMXTestUtils;
+
+import javax.jms.Connection;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.management.JMException;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class to test if any change in the broker JMX code is affesting the management console
+ * There are some hardcoding of management feature names and parameter names to create a customized
+ * look in the console.
+ */
+public class ManagementActorLoggingTest extends AbstractTestLogging
+{
+ private JMXTestUtils _jmxUtils;
+ private boolean _closed = false;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+ super.setUp();
+ _jmxUtils.open();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ if(!_closed)
+ {
+ _jmxUtils.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Description:
+ * When a connected client has its connection closed via the Management Console this will be logged as a CON-1002 message.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Connected Client
+ * 3. Connection is closed via Management Console
+ * Output:
+ *
+ * <date> CON-1002 : Close
+ *
+ * Validation Steps:
+ * 4. The CON ID is correct
+ * 5. This must be the last CON message for the Connection
+ * 6. It must be preceded by a CON-1001 for this Connection
+ *
+ * @throws Exception - {@see ManagedConnection.closeConnection and #getConnection}
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ */
+ public void testConnectionCloseViaManagement() throws IOException, Exception
+ {
+ //Create a connection to the broker
+ Connection connection = getConnection();
+
+ // Monitor the connection for an exception being thrown
+ // this should be a DisconnectionException but it is not this tests
+ // job to valiate that. Only use the exception as a synchronisation
+ // to check the log file for the Close message
+ final CountDownLatch exceptionReceived = new CountDownLatch(1);
+ connection.setExceptionListener(new ExceptionListener()
+ {
+ public void onException(JMSException e)
+ {
+ //Failover being attempted.
+ exceptionReceived.countDown();
+ }
+ });
+
+ //Remove the connection close from any 0-10 connections
+ _monitor.markDiscardPoint();
+
+ // Get a managedConnection
+ ManagedConnection mangedConnection = _jmxUtils.getManagedObject(ManagedConnection.class, "org.apache.qpid:type=VirtualHost.Connection,*");
+
+ //Close the connection
+ mangedConnection.closeConnection();
+
+ //Wait for the connection to close
+ assertTrue("Timed out waiting for conneciton to report close",
+ exceptionReceived.await(2, TimeUnit.SECONDS));
+
+ //Validate results
+ List<String> results = waitAndFindMatches("CON-1002");
+
+ assertEquals("Unexpected Connection Close count", 1, results.size());
+ }
+
+ /**
+ * Description:
+ * Exchange creation is possible from the Management Console.
+ * When an exchanged is created in this way then a EXH-1001 create message
+ * is expected to be logged.
+ * Input:
+ *
+ * 1. Running broker
+ * 2. Connected Management Console
+ * 3. Exchange Created via Management Console
+ * Output:
+ *
+ * EXH-1001 : Create : [Durable] Type:<value> Name:<value>
+ *
+ * Validation Steps:
+ * 4. The EXH ID is correct
+ * 5. The correct tags are present in the message based on the create options
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException
+ {
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createExchange("test", getName(), "direct", false);
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = waitAndFindMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+ public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous exchange declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createExchange("test", getName(), "topic", false);
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = waitAndFindMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+
+ public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous exchange declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createExchange("test", getName(), "fanout", false);
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = waitAndFindMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+
+ public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous exchange declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createExchange("test", getName(), "headers", false);
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = waitAndFindMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+
+ /**
+ * Description:
+ * Queue creation is possible from the Management Console. When a queue is created in this way then a QUE-1001 create message is expected to be logged.
+ * Input:
+ *
+ * 1. Running broker
+ * 2. Connected Management Console
+ * 3. Queue Created via Management Console
+ * Output:
+ *
+ * <date> QUE-1001 : Create : Transient Owner:<name>
+ *
+ * Validation Steps:
+ * 4. The QUE ID is correct
+ * 5. The correct tags are present in the message based on the create options
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createQueue("test", getName(), null, false);
+
+ // Validate
+
+ List<String> results = waitAndFindMatches("QUE-1001");
+
+ assertEquals("More than one queue creation found", 1, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct queue name
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+ /**
+ * Description:
+ * The ManagementConsole can be used to delete a queue. When this is done a QUE-1002 Deleted message must be logged.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Queue created on the broker with no subscribers
+ * 3. Management Console connected
+ * 4. Queue is deleted via Management Console
+ * Output:
+ *
+ * <date> QUE-1002 : Deleted
+ *
+ * Validation Steps:
+ * 5. The QUE ID is correct
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testQueueDeleteViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createQueue("test", getName(), null, false);
+
+ ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test");
+
+ managedBroker.deleteQueue(getName());
+
+ List<String> results = waitAndFindMatches("QUE-1002");
+
+ assertEquals("More than one queue deletion found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+
+ /**
+ * Description:
+ * The binding of a Queue and an Exchange is done via a Binding. When this Binding is created via the Management Console a BND-1001 Create message will be logged.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Connected Management Console
+ * 3. Use Management Console to perform binding
+ * Output:
+ *
+ * <date> BND-1001 : Create
+ *
+ * Validation Steps:
+ * 4. The BND ID is correct
+ * 5. This will be the first message for the given binding
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.createNewBinding}
+ */
+ public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createQueue("test", getName(), null, false);
+
+ ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.direct");
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = waitAndFindMatches("BND-1001");
+
+ assertEquals("Unexpected number of bindings logged", 2, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+ public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createQueue("test", getName(), null, false);
+
+ ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.topic");
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = waitAndFindMatches("BND-1001");
+
+ assertEquals("Unexpected number of bindings logged", 2, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+ public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createQueue("test", getName(), null, false);
+
+ ManagedExchange managedExchange = _jmxUtils.getManagedExchange("amq.fanout");
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = waitAndFindMatches("BND-1001");
+
+ assertEquals("Unexpected number of bindings logged", 2, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+ /**
+ * Description:
+ * Bindings can be deleted so that a queue can be rebound with a different set of values. This can be performed via the Management Console
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Management Console connected
+ * 3. Management Console is used to perform unbind.
+ * Output:
+ *
+ * <date> BND-1002 : Deleted
+ *
+ * Validation Steps:
+ * 4. The BND ID is correct
+ * 5. There must have been a BND-1001 Create message first.
+ * 6. This will be the last message for the given binding
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor or an issue with the JMX Connection
+ * @throws javax.management.JMException - {@see #createExchange and ManagedBroker.unregisterExchange}
+ */
+ public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException
+ {
+ //Remove any previous queue declares
+ _monitor.markDiscardPoint();
+
+ _jmxUtils.createExchange("test", getName(), "direct", false);
+
+ ManagedBroker managedBroker = _jmxUtils.getManagedBroker("test");
+
+ managedBroker.unregisterExchange(getName());
+
+ List<String> results = waitAndFindMatches("EXH-1002");
+
+ assertEquals("More than one exchange deletion found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java
new file mode 100644
index 0000000000..6100d5a23e
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java
@@ -0,0 +1,317 @@
+/*
+ *
+ * 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.systest.management.jmx;
+
+
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.AbstractTestLogging;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.util.LogMonitor;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Management Console Test Suite
+ *
+ * The Management Console test suite validates that the follow log messages as specified in the Functional Specification.
+ *
+ * This suite of tests validate that the management console messages occur correctly and according to the following format:
+ *
+ * MNG-1001 : Startup
+ * MNG-1002 : Starting : <service> : Listening on port <Port>
+ * MNG-1003 : Shutting down : <service> : port <Port>
+ * MNG-1004 : Ready
+ * MNG-1005 : Stopped
+ * MNG-1006 : Using SSL Keystore : <path>
+ * MNG-1007 : Open : User <username>
+ * MNG-1008 : Close : User <username>
+ */
+public class ManagementLoggingTest extends AbstractTestLogging
+{
+ private static final String MNG_PREFIX = "MNG-";
+
+ public void setUp() throws Exception
+ {
+ setLogMessagePrefix();
+
+ // We either do this here or have a null check in tearDown.
+ // As when this test is run against profiles other than java it will NPE
+ _monitor = new LogMonitor(_outputFile);
+ //We explicitly do not call super.setUp as starting up the broker is
+ //part of the test case.
+
+ }
+
+ /**
+ * Description:
+ * Using the startup configuration validate that the management startup
+ * message is logged correctly.
+ * Input:
+ * Standard configuration with management enabled
+ * Output:
+ *
+ * <date> MNG-1001 : Startup
+ *
+ * Constraints:
+ * This is the FIRST message logged by MNG
+ * Validation Steps:
+ *
+ * 1. The BRK ID is correct
+ * 2. This is the FIRST message logged by MNG
+ */
+ public void testManagementStartupEnabled() throws Exception
+ {
+ // This test only works on java brokers
+ if (isJavaBroker())
+ {
+ startBrokerAndCreateMonitor(true, false);
+
+ // Ensure we have received the MNG log msg.
+ waitForMessage("MNG-1001");
+
+ List<String> results = findMatches(MNG_PREFIX);
+ // Validation
+
+ assertTrue("MNGer message not logged", results.size() > 0);
+
+ String log = getLogMessage(results, 0);
+
+ //1
+ validateMessageID("MNG-1001", log);
+
+ //2
+ //There will be 2 copies of the startup message (one via SystemOut, and one via Log4J)
+ results = findMatches("MNG-1001");
+ assertEquals("Unexpected startup message count.",
+ 2, results.size());
+
+ //3
+ assertEquals("Startup log message is not 'Startup'.", "Startup",
+ getMessageString(log));
+ }
+ }
+
+ /**
+ * Description:
+ * Verify that when management is disabled in the configuration file the
+ * startup message is not logged.
+ * Input:
+ * Standard configuration with management disabled
+ * Output:
+ * NO MNG messages
+ * Validation Steps:
+ *
+ * 1. Validate that no MNG messages are produced.
+ */
+ public void testManagementStartupDisabled() throws Exception
+ {
+ if (isJavaBroker())
+ {
+ startBrokerAndCreateMonitor(false, false);
+
+ List<String> results = findMatches(MNG_PREFIX);
+ // Validation
+
+ assertEquals("MNGer messages logged", 0, results.size());
+ }
+ }
+
+ /**
+ * The two MNG-1002 messages are logged at the same time so lets test them
+ * at the same time.
+ *
+ * Description:
+ * Using the default configuration validate that the RMI Registry socket is
+ * correctly reported as being opened
+ *
+ * Input:
+ * The default configuration file
+ * Output:
+ *
+ * <date> MESSAGE MNG-1002 : Starting : RMI Registry : Listening on port 8999
+ *
+ * Constraints:
+ * The RMI ConnectorServer and Registry log messages do not have a prescribed order
+ * Validation Steps:
+ *
+ * 1. The MNG ID is correct
+ * 2. The specified port is the correct '8999'
+ *
+ * Description:
+ * Using the default configuration validate that the RMI ConnectorServer
+ * socket is correctly reported as being opened
+ *
+ * Input:
+ * The default configuration file
+ * Output:
+ *
+ * <date> MESSAGE MNG-1002 : Starting : RMI ConnectorServer : Listening on port 9099
+ *
+ * Constraints:
+ * The RMI ConnectorServer and Registry log messages do not have a prescribed order
+ * Validation Steps:
+ *
+ * 1. The MNG ID is correct
+ * 2. The specified port is the correct '9099'
+ */
+ public void testManagementStartupRMIEntries() throws Exception
+ {
+ if (isJavaBroker())
+ {
+ startBrokerAndCreateMonitor(true, false);
+
+ List<String> results = waitAndFindMatches("MNG-1002");
+ // Validation
+
+ //There will be 4 startup messages (two via SystemOut, and two via Log4J)
+ assertEquals("Unexpected MNG-1002 message count", 4, results.size());
+
+ String log = getLogMessage(results, 0);
+
+ //1
+ validateMessageID("MNG-1002", log);
+
+ //Check the RMI Registry port is as expected
+ int mPort = getManagementPort(getPort());
+ assertTrue("RMI Registry port not as expected(" + mPort + ").:" + getMessageString(log),
+ getMessageString(log).endsWith(String.valueOf(mPort)));
+
+ log = getLogMessage(results, 2);
+
+ //1
+ validateMessageID("MNG-1002", log);
+
+ // We expect the RMI Registry port (the defined 'management port') to be
+ // 100 lower than the JMX RMIConnector Server Port (the actual JMX server)
+ int jmxPort = mPort + ServerConfiguration.JMXPORT_CONNECTORSERVER_OFFSET;
+ assertTrue("JMX RMIConnectorServer port not as expected(" + jmxPort + ").:" + getMessageString(log),
+ getMessageString(log).endsWith(String.valueOf(jmxPort)));
+ }
+ }
+
+ /**
+ * Description:
+ * Using the default configuration with SSL enabled for the management port the SSL Keystore path should be reported via MNG-1006
+ * Input:
+ * Management SSL enabled default configuration.
+ * Output:
+ *
+ * <date> MESSAGE MNG-1006 : Using SSL Keystore : test_resources/ssl/keystore.jks
+ *
+ * Validation Steps:
+ *
+ * 1. The MNG ID is correct
+ * 2. The keystore path is as specified in the configuration
+ */
+ public void testManagementStartupSSLKeystore() throws Exception
+ {
+ if (isJavaBroker())
+ {
+ startBrokerAndCreateMonitor(true, true);
+
+ List<String> results = waitAndFindMatches("MNG-1006");
+
+ assertTrue("MNGer message not logged", results.size() > 0);
+
+ String log = getLogMessage(results, 0);
+
+ //1
+ validateMessageID("MNG-1006", log);
+
+ // Validate we only have two MNG-1002 (one via stdout, one via log4j)
+ results = findMatches("MNG-1006");
+ assertEquals("Upexpected SSL Keystore message count",
+ 2, results.size());
+
+ // Validate the keystore path is as expected
+ assertTrue("SSL Keystore entry expected.:" + getMessageString(log),
+ getMessageString(log).endsWith(new File(getConfigurationStringProperty("management.ssl.keyStorePath")).getName()));
+ }
+ }
+
+ /**
+ * Description: Tests the management connection open/close are logged correctly.
+ *
+ * Output:
+ *
+ * <date> MESSAGE MNG-1007 : Open : User <username>
+ * <date> MESSAGE MNG-1008 : Close : User <username>
+ *
+ * Validation Steps:
+ *
+ * 1. The MNG ID is correct
+ * 2. The message and username are correct
+ */
+ public void testManagementUserOpenClose() throws Exception
+ {
+ if (isJavaBroker())
+ {
+ startBrokerAndCreateMonitor(true, false);
+
+ final JMXTestUtils jmxUtils = new JMXTestUtils(this);
+ List<String> openResults = null;
+ List<String> closeResults = null;
+ try
+ {
+ jmxUtils.setUp();
+ jmxUtils.open();
+ openResults = waitAndFindMatches("MNG-1007");
+ }
+ finally
+ {
+ if (jmxUtils != null)
+ {
+ jmxUtils.close();
+ closeResults = waitAndFindMatches("MNG-1008");
+ }
+ }
+
+ assertNotNull("Management Open results null", openResults.size());
+ assertEquals("Management Open logged unexpected number of times", 1, openResults.size());
+
+ assertNotNull("Management Close results null", closeResults.size());
+ assertEquals("Management Close logged unexpected number of times", 1, closeResults.size());
+
+ final String openMessage = getMessageString(getLogMessage(openResults, 0));
+ assertTrue("Unexpected open message " + openMessage, openMessage.endsWith("Open : User admin"));
+ final String closeMessage = getMessageString(getLogMessage(closeResults, 0));
+ assertTrue("Unexpected close message " + closeMessage, closeMessage.endsWith("Close : User admin"));
+ }
+ }
+
+ private void startBrokerAndCreateMonitor(boolean managementEnabled, boolean useManagementSSL) throws Exception
+ {
+ //Ensure management is on
+ setConfigurationProperty("management.enabled", String.valueOf(managementEnabled));
+
+ if(useManagementSSL)
+ {
+ // This test requires we have an ssl connection
+ setConfigurationProperty("management.ssl.enabled", "true");
+ }
+
+ startBroker();
+
+ // Now we can create the monitor as _outputFile will now be defined
+ _monitor = new LogMonitor(_outputFile);
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java
new file mode 100644
index 0000000000..79d04b239e
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java
@@ -0,0 +1,696 @@
+/*
+ * 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.systest.management.jmx;
+
+import org.apache.commons.lang.time.FastDateFormat;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.configuration.ClientProperties;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.queue.NotificationCheckTest;
+import org.apache.qpid.server.queue.SimpleAMQQueueTest;
+import org.apache.qpid.test.client.destination.AddressBasedDestinationTest;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+import javax.jms.Connection;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+import javax.naming.NamingException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests the JMX API for the Managed Queue.
+ *
+ */
+public class QueueManagementTest extends QpidBrokerTestCase
+{
+
+ private static final Logger LOGGER = Logger.getLogger(QueueManagementTest.class);
+
+ private static final String VIRTUAL_HOST = "test";
+ private static final String TEST_QUEUE_DESCRIPTION = "my description";
+
+ private JMXTestUtils _jmxUtils;
+ private Connection _connection;
+ private Session _session;
+
+ private String _sourceQueueName;
+ private String _destinationQueueName;
+ private Destination _sourceQueue;
+ private Destination _destinationQueue;
+ private ManagedQueue _managedSourceQueue;
+ private ManagedQueue _managedDestinationQueue;
+
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+
+ super.setUp();
+ _sourceQueueName = getTestQueueName() + "_src";
+ _destinationQueueName = getTestQueueName() + "_dest";
+
+ createConnectionAndSession();
+
+ _sourceQueue = _session.createQueue(_sourceQueueName);
+ _destinationQueue = _session.createQueue(_destinationQueueName);
+ createQueueOnBroker(_sourceQueue);
+ createQueueOnBroker(_destinationQueue);
+
+ _jmxUtils.open();
+
+ createManagementInterfacesForQueues();
+ }
+
+ public void tearDown() throws Exception
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ super.tearDown();
+ }
+
+ public void testQueueAttributes() throws Exception
+ {
+ Queue queue = _session.createQueue(getTestQueueName());
+ createQueueOnBroker(queue);
+
+ final String queueName = queue.getQueueName();
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals("Unexpected name", queueName, managedQueue.getName());
+ assertEquals("Unexpected queue type", "standard", managedQueue.getQueueType());
+ }
+
+ public void testExclusiveQueueHasJmsClientIdAsOwner() throws Exception
+ {
+ Queue tmpQueue = _session.createTemporaryQueue();
+
+ final String queueName = tmpQueue.getQueueName();
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertNotNull(_connection.getClientID());
+ assertEquals("Unexpected owner", _connection.getClientID(), managedQueue.getOwner());
+ }
+
+ public void testNonExclusiveQueueHasNoOwner() throws Exception
+ {
+ Queue nonExclusiveQueue = _session.createQueue(getTestQueueName());
+ createQueueOnBroker(nonExclusiveQueue);
+
+ final String queueName = nonExclusiveQueue.getQueueName();
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertNull("Unexpected owner", managedQueue.getOwner());
+ }
+
+ public void testSetNewQueueDescriptionOnExistingQueue() throws Exception
+ {
+ Queue queue = _session.createQueue(getTestQueueName());
+ createQueueOnBroker(queue);
+
+ final String queueName = queue.getQueueName();
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertNull("Unexpected description", managedQueue.getDescription());
+
+ managedQueue.setDescription(TEST_QUEUE_DESCRIPTION);
+ assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription());
+ }
+
+ public void testNewQueueWithDescription() throws Exception
+ {
+ String queueName = getTestQueueName();
+ Map<String, Object> arguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_QUEUE_DESCRIPTION);
+ ((AMQSession<?, ?>)_session).createQueue(AMQShortString.valueOf(queueName), false, true, false, arguments);
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription());
+ }
+
+ /**
+ * Requires persistent store.
+ */
+ public void testQueueDescriptionSurvivesRestart() throws Exception
+ {
+ String queueName = getTestQueueName();
+ Map<String, Object> arguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_QUEUE_DESCRIPTION);
+
+ ((AMQSession<?, ?>)_session).createQueue(AMQShortString.valueOf(queueName), false, true, false, arguments);
+
+ ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription());
+
+ restartBroker();
+
+ managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals(TEST_QUEUE_DESCRIPTION, managedQueue.getDescription());
+ }
+
+ /**
+ * Tests queue creation with {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument. Also tests
+ * that the attribute is exposed correctly through {@link ManagedQueue#getMaximumDeliveryCount()}.
+ */
+ public void testCreateQueueWithMaximumDeliveryCountSet() throws Exception
+ {
+ final String queueName = getName();
+ final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST);
+
+ final Integer deliveryCount = 1;
+ final Map<String, Object> arguments = Collections.singletonMap(AMQQueueFactory.X_QPID_MAXIMUM_DELIVERY_COUNT, (Object)deliveryCount);
+ managedBroker.createNewQueue(queueName, null, true, arguments);
+
+ // Ensure the queue exists
+ assertNotNull("Queue object name expected to exist", _jmxUtils.getQueueObjectName("test", queueName));
+ assertNotNull("Manager queue expected to be available", _jmxUtils.getManagedQueue(queueName));
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals("Unexpected maximum delivery count", deliveryCount, managedQueue.getMaximumDeliveryCount());
+ }
+
+ /**
+ * Requires 0-10 as relies on ADDR addresses.
+ * @see AddressBasedDestinationTest for the testing of message routing to the alternate exchange
+ */
+ public void testGetSetAlternateExchange() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String altExchange = "amq.fanout";
+ String addrWithAltExch = String.format("ADDR:%s;{create:always,node:{type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName, altExchange);
+ Queue queue = _session.createQueue(addrWithAltExch);
+
+ createQueueOnBroker(queue);
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals("Newly created queue does not have expected alternate exchange", altExchange, managedQueue.getAlternateExchange());
+
+ String newAltExch = "amq.topic";
+ managedQueue.setAlternateExchange(newAltExch);
+ assertEquals("Unexpected alternate exchange after set", newAltExch, managedQueue.getAlternateExchange());
+ }
+
+ /**
+ * Requires 0-10 as relies on ADDR addresses.
+ */
+ public void testRemoveAlternateExchange() throws Exception
+ {
+ String queueName = getTestQueueName();
+ String altExchange = "amq.fanout";
+ String addrWithAltExch = String.format("ADDR:%s;{create:always,node:{type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName, altExchange);
+ Queue queue = _session.createQueue(addrWithAltExch);
+
+ createQueueOnBroker(queue);
+
+ final ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ assertEquals("Newly created queue does not have expected alternate exchange", altExchange, managedQueue.getAlternateExchange());
+
+ managedQueue.setAlternateExchange("");
+ assertNull("Unexpected alternate exchange after set", managedQueue.getAlternateExchange());
+ }
+
+ /**
+ * Requires persistent store
+ * Requires 0-10 as relies on ADDR addresses.
+ */
+ public void testAlternateExchangeSurvivesRestart() throws Exception
+ {
+ String nonMandatoryExchangeName = "exch" + getName();
+
+ final ManagedBroker managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST);
+ managedBroker.createNewExchange(nonMandatoryExchangeName, "fanout", true);
+
+ String queueName1 = getTestQueueName() + "1";
+ String altExchange1 = "amq.fanout";
+ String addr1WithAltExch = String.format("ADDR:%s;{create:always,node:{durable: true,type:queue,x-declare:{alternate-exchange:'%s'}}}", queueName1, altExchange1);
+ Queue queue1 = _session.createQueue(addr1WithAltExch);
+
+ String queueName2 = getTestQueueName() + "2";
+ String addr2WithoutAltExch = String.format("ADDR:%s;{create:always,node:{durable: true,type:queue}}", queueName2);
+ Queue queue2 = _session.createQueue(addr2WithoutAltExch);
+
+ createQueueOnBroker(queue1);
+ createQueueOnBroker(queue2);
+
+ ManagedQueue managedQueue1 = _jmxUtils.getManagedQueue(queueName1);
+ assertEquals("Newly created queue1 does not have expected alternate exchange", altExchange1, managedQueue1.getAlternateExchange());
+
+ ManagedQueue managedQueue2 = _jmxUtils.getManagedQueue(queueName2);
+ assertNull("Newly created queue2 does not have expected alternate exchange", managedQueue2.getAlternateExchange());
+
+ String altExchange2 = nonMandatoryExchangeName;
+ managedQueue2.setAlternateExchange(altExchange2);
+
+ restartBroker();
+
+ managedQueue1 = _jmxUtils.getManagedQueue(queueName1);
+ assertEquals("Queue1 does not have expected alternate exchange after restart", altExchange1, managedQueue1.getAlternateExchange());
+
+ managedQueue2 = _jmxUtils.getManagedQueue(queueName2);
+ assertEquals("Queue2 does not have expected updated alternate exchange after restart", altExchange2, managedQueue2.getAlternateExchange());
+ }
+
+ /**
+ * Tests the ability to receive queue alerts as JMX notifications.
+ *
+ * @see NotificationCheckTest
+ * @see SimpleAMQQueueTest#testNotificationFiredAsync()
+ * @see SimpleAMQQueueTest#testNotificationFiredOnEnqueue()
+ */
+ public void testQueueNotification() throws Exception
+ {
+ final String queueName = getName();
+ final long maximumMessageCount = 3;
+
+ Queue queue = _session.createQueue(queueName);
+ createQueueOnBroker(queue);
+
+ ManagedQueue managedQueue = _jmxUtils.getManagedQueue(queueName);
+ managedQueue.setMaximumMessageCount(maximumMessageCount);
+
+ RecordingNotificationListener listener = new RecordingNotificationListener(1);
+
+ _jmxUtils.addNotificationListener(_jmxUtils.getQueueObjectName(VIRTUAL_HOST, queueName), listener, null, null);
+
+ // Send two messages - this should *not* trigger the notification
+ sendMessage(_session, queue, 2);
+
+ assertEquals("Premature notification received", 0, listener.getNumberOfNotificationsReceived());
+
+ // A further message should trigger the message count alert
+ sendMessage(_session, queue, 1);
+
+ listener.awaitExpectedNotifications(5, TimeUnit.SECONDS);
+
+ assertEquals("Unexpected number of JMX notifications received", 1, listener.getNumberOfNotificationsReceived());
+
+ Notification notification = listener.getLastNotification();
+ assertEquals("Unexpected notification message", "MESSAGE_COUNT_ALERT 3: Maximum count on queue threshold (3) breached.", notification.getMessage());
+ }
+
+ /**
+ * Tests {@link ManagedQueue#viewMessages(long, long)} interface.
+ */
+ public void testViewSingleMessage() throws Exception
+ {
+ final List<Message> sentMessages = sendMessage(_session, _sourceQueue, 1);
+ syncSession(_session);
+ final Message sentMessage = sentMessages.get(0);
+
+ assertEquals("Unexpected queue depth", 1, _managedSourceQueue.getMessageCount().intValue());
+
+ // Check the contents of the message
+ final TabularData tab = _managedSourceQueue.viewMessages(1l, 1l);
+ assertEquals("Unexpected number of rows in table", 1, tab.size());
+ final Iterator<CompositeData> rowItr = (Iterator<CompositeData>) tab.values().iterator();
+
+ final CompositeData row1 = rowItr.next();
+ assertNotNull("Message should have AMQ message id", row1.get(ManagedQueue.MSG_AMQ_ID));
+ assertEquals("Unexpected queue position", 1l, row1.get(ManagedQueue.MSG_QUEUE_POS));
+ assertEquals("Unexpected redelivered flag", Boolean.FALSE, row1.get(ManagedQueue.MSG_REDELIVERED));
+
+ // Check the contents of header (encoded in a string array)
+ final String[] headerArray = (String[]) row1.get(ManagedQueue.MSG_HEADER);
+ assertNotNull("Expected message header array", headerArray);
+ final Map<String, String> headers = headerArrayToMap(headerArray);
+
+ final String expectedJMSMessageID = isBroker010() ? sentMessage.getJMSMessageID().replace("ID:", "") : sentMessage.getJMSMessageID();
+ final String expectedFormattedJMSTimestamp = FastDateFormat.getInstance(ManagedQueue.JMSTIMESTAMP_DATETIME_FORMAT).format(sentMessage.getJMSTimestamp());
+ assertEquals("Unexpected JMSMessageID within header", expectedJMSMessageID, headers.get("JMSMessageID"));
+ assertEquals("Unexpected JMSPriority within header", String.valueOf(sentMessage.getJMSPriority()), headers.get("JMSPriority"));
+ assertEquals("Unexpected JMSTimestamp within header", expectedFormattedJMSTimestamp, headers.get("JMSTimestamp"));
+ }
+
+ /**
+ * Tests {@link ManagedQueue#moveMessages(long, long, String)} interface.
+ */
+ public void testMoveMessagesBetweenQueues() throws Exception
+ {
+ final int numberOfMessagesToSend = 10;
+
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+
+ // Move first three messages to destination
+ long fromMessageId = amqMessagesIds.get(0);
+ long toMessageId = amqMessagesIds.get(2);
+ _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName);
+
+ assertEquals("Unexpected queue depth on destination queue after first move", 3, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after first move", 7, _managedSourceQueue.getMessageCount().intValue());
+
+ // Now move a further two messages to destination
+ fromMessageId = amqMessagesIds.get(7);
+ toMessageId = amqMessagesIds.get(8);
+ _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName);
+ assertEquals("Unexpected queue depth on destination queue after second move", 5, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after second move", 5, _managedSourceQueue.getMessageCount().intValue());
+
+ assertMessageIndicesOn(_destinationQueue, 0, 1, 2, 7, 8);
+ }
+
+ /**
+ * Tests {@link ManagedQueue#copyMessages(long, long, String)} interface.
+ */
+ public void testCopyMessagesBetweenQueues() throws Exception
+ {
+ final int numberOfMessagesToSend = 10;
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+
+ // Copy first three messages to destination
+ long fromMessageId = amqMessagesIds.get(0);
+ long toMessageId = amqMessagesIds.get(2);
+ _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName);
+
+ assertEquals("Unexpected queue depth on destination queue after first copy", 3, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after first copy", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ // Now copy a further two messages to destination
+ fromMessageId = amqMessagesIds.get(7);
+ toMessageId = amqMessagesIds.get(8);
+ _managedSourceQueue.copyMessages(fromMessageId, toMessageId, _destinationQueueName);
+ assertEquals("Unexpected queue depth on destination queue after second copy", 5, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after second copy", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ assertMessageIndicesOn(_destinationQueue, 0, 1, 2, 7, 8);
+ }
+
+ public void testMoveMessagesBetweenQueuesWithActiveConsumerOnSourceQueue() throws Exception
+ {
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, new Integer(1).toString());
+ Connection asyncConnection = getConnection();
+ asyncConnection.start();
+
+ final int numberOfMessagesToSend = 50;
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+
+ long fromMessageId = amqMessagesIds.get(0);
+ long toMessageId = amqMessagesIds.get(numberOfMessagesToSend - 1);
+
+ CountDownLatch consumerReadToHalfwayLatch = new CountDownLatch(numberOfMessagesToSend / 2);
+ AtomicInteger totalConsumed = new AtomicInteger(0);
+ startAsyncConsumerOn(_sourceQueue, asyncConnection, consumerReadToHalfwayLatch, totalConsumed);
+
+ boolean halfwayPointReached = consumerReadToHalfwayLatch.await(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Did not read half of messages within time allowed", halfwayPointReached);
+
+ _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName);
+
+ asyncConnection.stop();
+
+ // The exact number of messages moved will be non deterministic, as the number of messages processed
+ // by the consumer cannot be predicted. There is also the possibility that a message can remain
+ // on the source queue. This situation will arise if a message has been acquired by the consumer, but not
+ // yet delivered to the client application (i.e. MessageListener#onMessage()) when the Connection#stop() occurs.
+ //
+ // The number of messages moved + the number consumed + any messages remaining on source should
+ // *always* be equal to the number we originally sent.
+
+ int numberOfMessagesReadByConsumer = totalConsumed.intValue();
+ int numberOfMessagesOnDestinationQueue = _managedDestinationQueue.getMessageCount().intValue();
+ int numberOfMessagesRemainingOnSourceQueue = _managedSourceQueue.getMessageCount().intValue();
+
+ LOGGER.debug("Async consumer read : " + numberOfMessagesReadByConsumer
+ + " Number of messages moved to destination : " + numberOfMessagesOnDestinationQueue
+ + " Number of messages remaining on source : " + numberOfMessagesRemainingOnSourceQueue);
+ assertEquals("Unexpected number of messages after move", numberOfMessagesToSend, numberOfMessagesReadByConsumer + numberOfMessagesOnDestinationQueue + numberOfMessagesRemainingOnSourceQueue);
+ }
+
+ public void testMoveMessagesBetweenQueuesWithActiveConsumerOnDestinationQueue() throws Exception
+ {
+ setTestClientSystemProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, new Integer(1).toString());
+ Connection asyncConnection = getConnection();
+ asyncConnection.start();
+
+ final int numberOfMessagesToSend = 50;
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+ long fromMessageId = amqMessagesIds.get(0);
+ long toMessageId = amqMessagesIds.get(numberOfMessagesToSend - 1);
+
+ AtomicInteger totalConsumed = new AtomicInteger(0);
+ CountDownLatch allMessagesConsumedLatch = new CountDownLatch(numberOfMessagesToSend);
+ startAsyncConsumerOn(_destinationQueue, asyncConnection, allMessagesConsumedLatch, totalConsumed);
+
+ _managedSourceQueue.moveMessages(fromMessageId, toMessageId, _destinationQueueName);
+
+ allMessagesConsumedLatch.await(5000, TimeUnit.MILLISECONDS);
+ assertEquals("Did not consume all messages from destination queue", numberOfMessagesToSend, totalConsumed.intValue());
+ }
+
+ /**
+ * Tests {@link ManagedQueue#moveMessages(long, long, String)} interface.
+ */
+ public void testMoveMessageBetweenQueuesWithBrokerRestart() throws Exception
+ {
+ final int numberOfMessagesToSend = 1;
+
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ restartBroker();
+
+ createManagementInterfacesForQueues();
+ createConnectionAndSession();
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+
+ // Move messages to destination
+ long messageId = amqMessagesIds.get(0);
+ _managedSourceQueue.moveMessages(messageId, messageId, _destinationQueueName);
+
+ assertEquals("Unexpected queue depth on destination queue after move", 1, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after move", 0, _managedSourceQueue.getMessageCount().intValue());
+
+ assertMessageIndicesOn(_destinationQueue, 0);
+ }
+
+ /**
+ * Tests {@link ManagedQueue#copyMessages(long, long, String)} interface.
+ */
+ public void testCopyMessageBetweenQueuesWithBrokerRestart() throws Exception
+ {
+ final int numberOfMessagesToSend = 1;
+
+ sendMessage(_session, _sourceQueue, numberOfMessagesToSend);
+ syncSession(_session);
+ assertEquals("Unexpected queue depth after send", numberOfMessagesToSend, _managedSourceQueue.getMessageCount().intValue());
+
+ restartBroker();
+
+ createManagementInterfacesForQueues();
+ createConnectionAndSession();
+
+ List<Long> amqMessagesIds = getAMQMessageIdsOn(_managedSourceQueue, 1, numberOfMessagesToSend);
+
+ // Move messages to destination
+ long messageId = amqMessagesIds.get(0);
+ _managedSourceQueue.copyMessages(messageId, messageId, _destinationQueueName);
+
+ assertEquals("Unexpected queue depth on destination queue after copy", 1, _managedDestinationQueue.getMessageCount().intValue());
+ assertEquals("Unexpected queue depth on source queue after copy", 1, _managedSourceQueue.getMessageCount().intValue());
+
+ assertMessageIndicesOn(_destinationQueue, 0);
+ }
+
+ @Override
+ public Message createNextMessage(Session session, int messageNumber) throws JMSException
+ {
+ Message message = session.createTextMessage(getContentForMessageNumber(messageNumber));
+ message.setIntProperty(INDEX, messageNumber);
+ return message;
+ }
+
+ private void startAsyncConsumerOn(Destination queue, Connection asyncConnection,
+ final CountDownLatch requiredNumberOfMessagesRead, final AtomicInteger totalConsumed) throws Exception
+ {
+ Session session = asyncConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageConsumer consumer = session.createConsumer(queue);
+ consumer.setMessageListener(new MessageListener()
+ {
+
+ @Override
+ public void onMessage(Message arg0)
+ {
+ totalConsumed.incrementAndGet();
+ requiredNumberOfMessagesRead.countDown();
+ }
+ });
+ }
+
+ private void assertMessageIndicesOn(Destination queue, int... expectedIndices) throws Exception
+ {
+ MessageConsumer consumer = _session.createConsumer(queue);
+
+ for (int i : expectedIndices)
+ {
+ TextMessage message = (TextMessage)consumer.receive(1000);
+ assertNotNull("Expected message with index " + i, message);
+ assertEquals("Expected message with index " + i, i, message.getIntProperty(INDEX));
+ assertEquals("Expected message content", getContentForMessageNumber(i), message.getText());
+ }
+
+ assertNull("Unexpected message encountered", consumer.receive(1000));
+ }
+
+ private List<Long> getAMQMessageIdsOn(ManagedQueue managedQueue, long startIndex, long endIndex) throws Exception
+ {
+ final SortedSet<Long> messageIds = new TreeSet<Long>();
+
+ final TabularData tab = managedQueue.viewMessages(startIndex, endIndex);
+ final Iterator<CompositeData> rowItr = (Iterator<CompositeData>) tab.values().iterator();
+ while(rowItr.hasNext())
+ {
+ final CompositeData row = rowItr.next();
+ long amqMessageId = (Long)row.get(ManagedQueue.MSG_AMQ_ID);
+ messageIds.add(amqMessageId);
+ }
+
+ return new ArrayList<Long>(messageIds);
+ }
+
+ /**
+ *
+ * Utility method to convert array of Strings in the form x = y into a
+ * map with key/value x =&gt; y.
+ *
+ */
+ private Map<String,String> headerArrayToMap(final String[] headerArray)
+ {
+ final Map<String, String> headerMap = new HashMap<String, String>();
+ final List<String> headerList = Arrays.asList(headerArray);
+ for (Iterator<String> iterator = headerList.iterator(); iterator.hasNext();)
+ {
+ final String nameValuePair = iterator.next();
+ final String[] nameValue = nameValuePair.split(" *= *", 2);
+ headerMap.put(nameValue[0], nameValue[1]);
+ }
+ return headerMap;
+ }
+
+ private void createQueueOnBroker(Destination destination) throws JMSException
+ {
+ _session.createConsumer(destination).close(); // Create a consumer only to cause queue creation
+ }
+
+ private void syncSession(Session session) throws Exception
+ {
+ ((AMQSession<?,?>)session).sync();
+ }
+
+ private void createConnectionAndSession() throws JMSException,
+ NamingException
+ {
+ _connection = getConnection();
+ _connection.start();
+ _session = _connection.createSession(true, Session.SESSION_TRANSACTED);
+ }
+
+ private void createManagementInterfacesForQueues()
+ {
+ _managedSourceQueue = _jmxUtils.getManagedQueue(_sourceQueueName);
+ _managedDestinationQueue = _jmxUtils.getManagedQueue(_destinationQueueName);
+ }
+
+ private String getContentForMessageNumber(int msgCount)
+ {
+ return "Message count " + msgCount;
+ }
+
+ private final class RecordingNotificationListener implements NotificationListener
+ {
+ private final CountDownLatch _notificationReceivedLatch;
+ private final AtomicInteger _numberOfNotifications;
+ private final AtomicReference<Notification> _lastNotification;
+
+ private RecordingNotificationListener(int expectedNumberOfNotifications)
+ {
+ _notificationReceivedLatch = new CountDownLatch(expectedNumberOfNotifications);
+ _numberOfNotifications = new AtomicInteger(0);
+ _lastNotification = new AtomicReference<Notification>();
+ }
+
+ @Override
+ public void handleNotification(Notification notification, Object handback)
+ {
+ _lastNotification.set(notification);
+ _numberOfNotifications.incrementAndGet();
+ _notificationReceivedLatch.countDown();
+ }
+
+ public int getNumberOfNotificationsReceived()
+ {
+ return _numberOfNotifications.get();
+ }
+
+ public Notification getLastNotification()
+ {
+ return _lastNotification.get();
+ }
+
+ public void awaitExpectedNotifications(long timeout, TimeUnit timeunit) throws InterruptedException
+ {
+ _notificationReceivedLatch.await(timeout, timeunit);
+ }
+ }
+
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java
new file mode 100644
index 0000000000..c3fff94923
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.systest.management.jmx;
+
+import java.util.List;
+
+import javax.jms.Connection;
+import javax.jms.MessageConsumer;
+import javax.jms.Queue;
+import javax.jms.Session;
+
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.management.common.mbeans.ServerInformation;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+
+public class StatisticsTest extends QpidBrokerTestCase
+{
+ private static final String TEST_USER = "admin";
+ private static final String TEST_PASSWORD = "admin";
+ private static final int MESSAGE_COUNT_TEST = 5;
+ private static final int MESSAGE_COUNT_DEV = 9;
+
+ private JMXTestUtils _jmxUtils;
+ private Connection _test1, _dev;
+ private Session _testSession, _developmentSession;
+ private Queue _developmentQueue, _testQueue;
+ protected String _brokerUrl;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this, TEST_USER, TEST_PASSWORD);
+ _jmxUtils.setUp();
+
+ super.setUp();
+
+ _brokerUrl = getBroker().toString();
+ _test1 = new AMQConnection(_brokerUrl, TEST_USER, TEST_PASSWORD, "clientid", "test");
+ _dev = new AMQConnection(_brokerUrl, TEST_USER, TEST_PASSWORD, "clientid", "development");
+ _test1.start();
+ _dev.start();
+
+ _testSession = _test1.createSession(true, Session.SESSION_TRANSACTED);
+ _developmentSession = _dev.createSession(true, Session.SESSION_TRANSACTED);
+
+ _developmentQueue = _developmentSession.createQueue(getTestQueueName());
+ _testQueue = _testSession.createQueue(getTestQueueName());
+
+ //Create queues by opening and closing consumers
+ final MessageConsumer testConsumer = _testSession.createConsumer(_testQueue);
+ testConsumer.close();
+ final MessageConsumer developmentConsumer = _developmentSession.createConsumer(_developmentQueue);
+ developmentConsumer.close();
+
+ _jmxUtils.open();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ _jmxUtils.close();
+
+ super.tearDown();
+ }
+
+ public void testInitialStatisticValues() throws Exception
+ {
+ //Check initial values
+ checkSingleConnectionOnVHostStatistics("test", 0, 0, 0, 0);
+ checkVHostStatistics("test", 0, 0, 0, 0);
+ checkSingleConnectionOnVHostStatistics("development", 0, 0, 0, 0);
+ checkVHostStatistics("development", 0, 0, 0, 0);
+ checkBrokerStatistics(0, 0, 0, 0);
+ }
+
+ public void testSendOnSingleVHost() throws Exception
+ {
+ sendMessagesAndSync(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+
+ //Check values
+ checkSingleConnectionOnVHostStatistics("test", MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0);
+ checkVHostStatistics("test", MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0);
+ checkSingleConnectionOnVHostStatistics("development", 0, 0, 0, 0);
+ checkVHostStatistics("development", 0, 0, 0, 0);
+ checkBrokerStatistics(MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0);
+ }
+
+ public void testSendOnTwoVHosts() throws Exception
+ {
+ sendMessagesAndSync(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+ sendMessagesAndSync(_developmentSession, _developmentQueue, MESSAGE_COUNT_DEV);
+
+ //Check values
+ checkSingleConnectionOnVHostStatistics("test", MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0);
+ checkVHostStatistics("test", MESSAGE_COUNT_TEST, 0, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, 0);
+ checkSingleConnectionOnVHostStatistics("development", MESSAGE_COUNT_DEV, 0, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, 0);
+ checkVHostStatistics("development", MESSAGE_COUNT_DEV, 0, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, 0);
+ checkBrokerStatistics(MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, 0, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE, 0);
+ }
+
+ public void testSendAndConsumeOnSingleVHost() throws Exception
+ {
+ sendMessagesAndSync(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+ consumeMessages(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+
+ //Check values
+ checkSingleConnectionOnVHostStatistics("test", MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE);
+ checkVHostStatistics("test", MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE);
+ checkSingleConnectionOnVHostStatistics("development", 0, 0, 0, 0);
+ checkVHostStatistics("development", 0, 0, 0, 0);
+ checkBrokerStatistics(MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE);
+ }
+
+ public void testSendAndConsumeOnTwoVHosts() throws Exception
+ {
+ sendMessagesAndSync(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+ sendMessagesAndSync(_developmentSession, _developmentQueue, MESSAGE_COUNT_DEV);
+ consumeMessages(_testSession, _testQueue, MESSAGE_COUNT_TEST);
+ consumeMessages(_developmentSession, _developmentQueue, MESSAGE_COUNT_DEV);
+
+ //Check values
+ checkSingleConnectionOnVHostStatistics("test", MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE);
+ checkVHostStatistics("test", MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_TEST * DEFAULT_MESSAGE_SIZE);
+ checkSingleConnectionOnVHostStatistics("development", MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE);
+ checkVHostStatistics("development", MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE, MESSAGE_COUNT_DEV * DEFAULT_MESSAGE_SIZE);
+ checkBrokerStatistics(MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE, (MESSAGE_COUNT_TEST + MESSAGE_COUNT_DEV) * DEFAULT_MESSAGE_SIZE);
+ }
+
+ private void sendMessagesAndSync(Session session, Queue queue, int numberOfMessages) throws Exception
+ {
+ //Send messages via connection on and sync
+ sendMessage(session, queue, numberOfMessages);
+ ((AMQSession<?,?>)session).sync();
+ }
+
+ private void consumeMessages(Session session, Queue queue, int numberOfMessages) throws Exception
+ {
+ //consume the messages on the virtual host
+ final MessageConsumer consumer = session.createConsumer(queue);
+ for (int i = 0 ; i < numberOfMessages ; i++)
+ {
+ assertNotNull("an expected message was not received", consumer.receive(1500));
+ }
+ session.commit();
+ consumer.close();
+ }
+
+ private void checkSingleConnectionOnVHostStatistics(String vHostName, long messagesSent, long messagesReceived, long dataSent, long dataReceived)
+ {
+ List<ManagedConnection> managedConnections = _jmxUtils.getManagedConnections(vHostName);
+ assertEquals(1, managedConnections.size());
+
+ ManagedConnection managedConnection = managedConnections.get(0);
+
+ assertEquals(messagesSent, managedConnection.getTotalMessagesReceived());
+ assertEquals(messagesReceived, managedConnection.getTotalMessagesDelivered());
+
+ assertEquals(dataSent, managedConnection.getTotalDataReceived());
+ assertEquals(dataReceived, managedConnection.getTotalDataDelivered());
+ }
+
+ private void checkVHostStatistics(String vHostName, long messagesSent, long messagesReceived, long dataSent, long dataReceived)
+ {
+ ManagedBroker vhost = _jmxUtils.getManagedBroker(vHostName);
+
+ assertEquals(messagesSent, vhost.getTotalMessagesReceived());
+ assertEquals(messagesReceived, vhost.getTotalMessagesDelivered());
+
+ assertEquals(dataSent, vhost.getTotalDataReceived());
+ assertEquals(dataReceived, vhost.getTotalDataDelivered());
+ }
+
+ private void checkBrokerStatistics(long messagesSent, long messagesReceived, long dataSent, long dataReceived)
+ {
+ ServerInformation broker = _jmxUtils.getServerInformation();
+
+ assertEquals(messagesSent, broker.getTotalMessagesReceived());
+ assertEquals(messagesReceived, broker.getTotalMessagesDelivered());
+
+ assertEquals(dataSent, broker.getTotalDataReceived());
+ assertEquals(dataReceived, broker.getTotalDataDelivered());
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java
new file mode 100644
index 0000000000..62b1b554a9
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.systest.management.jmx;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.jms.Connection;
+import javax.jms.JMSException;
+
+import org.apache.qpid.management.common.mbeans.UserManagement;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.tools.security.Passwd;
+
+/**
+ * System test for User Management.
+ *
+ */
+public class UserManagementTest extends QpidBrokerTestCase
+{
+ private static final String TEST_NEWPASSWORD = "newpassword";
+ private static final String TEST_PASSWORD = "password";
+ private JMXTestUtils _jmxUtils;
+ private String _testUserName;
+ private File _passwordFile;
+ private UserManagement _userManagement;
+ private Passwd _passwd;
+
+ public void setUp() throws Exception
+ {
+ _passwd = createPasswordEncodingUtility();
+ _passwordFile = createTemporaryPasswordFileWithJmxAdminUser();
+
+ setConfigurationProperty("security.pd-auth-manager.principal-database.class", getPrincipalDatabaseImplClass().getName());
+ setConfigurationProperty("security.pd-auth-manager.principal-database.attributes.attribute.name", "passwordFile");
+ setConfigurationProperty("security.pd-auth-manager.principal-database.attributes.attribute.value", _passwordFile.getAbsolutePath());
+
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+
+ super.setUp();
+ _jmxUtils.open();
+
+ _testUserName = getTestName() + System.currentTimeMillis();
+
+ _userManagement = _jmxUtils.getUserManagement();
+ }
+
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testCreateUser() throws Exception
+ {
+ final int initialNumberOfUsers = _userManagement.viewUsers().size();
+ assertFileDoesNotContainsPasswordForUser(_testUserName);
+
+ boolean success = _userManagement.createUser(_testUserName, TEST_PASSWORD);
+ assertTrue("Should have been able to create new user " + _testUserName, success);
+ assertEquals("Unexpected number of users after add", initialNumberOfUsers + 1, _userManagement.viewUsers().size());
+
+ assertFileContainsPasswordForUser(_testUserName);
+ }
+
+ public void testJmsLoginForNewUser() throws Exception
+ {
+ assertJmsConnectionFails(_testUserName, TEST_PASSWORD);
+ testCreateUser();
+
+ assertJmsConnectionSucceeds(_testUserName, TEST_PASSWORD);
+ }
+
+ public void testDeleteUser() throws Exception
+ {
+ final int initialNumberOfUsers = _userManagement.viewUsers().size();
+
+ testCreateUser();
+
+ boolean success = _userManagement.deleteUser(_testUserName);
+ assertTrue("Should have been able to delete new user " + _testUserName, success);
+ assertEquals("Unexpected number of users after delete", initialNumberOfUsers, _userManagement.viewUsers().size());
+ assertFileDoesNotContainsPasswordForUser(_testUserName);
+ }
+
+ public void testJmsLoginNotPossibleForDeletedUser() throws Exception
+ {
+ testDeleteUser();
+
+ assertJmsConnectionFails(_testUserName, TEST_PASSWORD);
+ }
+
+ public void testSetPassword() throws Exception
+ {
+ testCreateUser();
+
+ _userManagement.setPassword(_testUserName, TEST_NEWPASSWORD);
+
+ assertFileContainsPasswordForUser(_testUserName);
+ }
+
+ public void testJmsLoginForPasswordChangedUser() throws Exception
+ {
+ testSetPassword();
+
+ assertJmsConnectionSucceeds(_testUserName, TEST_NEWPASSWORD);
+ assertJmsConnectionFails(_testUserName, TEST_PASSWORD);
+ }
+
+ public void testReload() throws Exception
+ {
+ writePasswordFile(_passwordFile, JMXTestUtils.DEFAULT_USERID, JMXTestUtils.DEFAULT_PASSWORD, _testUserName, TEST_PASSWORD);
+
+ assertJmsConnectionFails(_testUserName, TEST_PASSWORD);
+
+ _userManagement.reloadData();
+
+ assertJmsConnectionSucceeds(_testUserName, TEST_PASSWORD);
+ }
+
+ protected Passwd createPasswordEncodingUtility()
+ {
+ return new Passwd()
+ {
+ @Override
+ public String getOutput(String username, String password)
+ {
+ return username + ":" + password;
+ }
+ };
+ }
+
+ protected Class<? extends PrincipalDatabase> getPrincipalDatabaseImplClass()
+ {
+ return PlainPasswordFilePrincipalDatabase.class;
+ }
+
+ private File createTemporaryPasswordFileWithJmxAdminUser() throws Exception
+ {
+ File passwordFile = File.createTempFile("passwd", "pwd");
+ passwordFile.deleteOnExit();
+ writePasswordFile(passwordFile, JMXTestUtils.DEFAULT_USERID, JMXTestUtils.DEFAULT_PASSWORD);
+ return passwordFile;
+ }
+
+ private void writePasswordFile(File passwordFile, String... userNamePasswordPairs) throws Exception
+ {
+ FileWriter writer = null;
+ try
+ {
+ writer = new FileWriter(passwordFile);
+ for (int i = 0; i < userNamePasswordPairs.length; i=i+2)
+ {
+ String username = userNamePasswordPairs[i];
+ String password = userNamePasswordPairs[i+1];
+ writer.append(_passwd.getOutput(username, password) + "\n");
+ }
+ }
+ finally
+ {
+ writer.close();
+ }
+ }
+
+
+ private void assertFileContainsPasswordForUser(String username) throws IOException
+ {
+ assertTrue("Could not find password for user " + username + " within " + _passwordFile, passwordFileContainsUser(username));
+ }
+
+ private void assertFileDoesNotContainsPasswordForUser(String username) throws IOException
+ {
+ assertFalse("Could not find password for user " + username + " within " + _passwordFile, passwordFileContainsUser(username));
+ }
+
+ private boolean passwordFileContainsUser(String username) throws IOException
+ {
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line = reader.readLine();
+ while(line != null)
+ {
+ if (line.startsWith(username))
+ {
+ return true;
+ }
+ line = reader.readLine();
+ }
+
+ return false;
+ }
+ finally
+ {
+ reader.close();
+ }
+ }
+
+ private void assertJmsConnectionSucceeds(String username, String password) throws Exception
+ {
+ Connection connection = getConnection(username, password);
+ assertNotNull(connection);
+ }
+
+ private void assertJmsConnectionFails(String username, String password) throws Exception
+ {
+ try
+ {
+ getConnection(username, password);
+ fail("Exception not thrown");
+ }
+ catch (JMSException e)
+ {
+ // PASS
+ }
+ }
+}
diff --git a/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java
new file mode 100644
index 0000000000..84a66232ce
--- /dev/null
+++ b/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.systest.management.jmx;
+
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.tools.security.Passwd;
+
+public class UserManagementWithBase64MD5PasswordsTest extends UserManagementTest
+{
+ @Override
+ protected Passwd createPasswordEncodingUtility()
+ {
+ return new Passwd();
+ }
+
+ @Override
+ protected Class<? extends PrincipalDatabase> getPrincipalDatabaseImplClass()
+ {
+ return Base64MD5PasswordFilePrincipalDatabase.class;
+ }
+
+}