From 07c285f662e8f60d4e8aca247b65b77ca5df4587 Mon Sep 17 00:00:00 2001 From: Robert Gemmell Date: Thu, 28 Jun 2012 16:46:12 +0000 Subject: QPID-3998, QPID-3999, QPID-4093: add new management plugins for jmx/rest/webui functionality, partial merge from the java-config-and-management branch at r1355039 git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1355072 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/java/bdbstore/build.xml | 2 +- qpid/java/bdbstore/jmx/MANIFEST.MF | 20 + qpid/java/bdbstore/jmx/build.xml | 29 + .../jmx/BDBHAMessageStoreManagerMBean.java | 232 ++++++ .../jmx/BDBHAMessageStoreManagerMBeanProvider.java | 73 ++ .../berkeleydb/jmx/ManagedBDBHAMessageStore.java | 82 ++ .../org.apache.qpid.server.jmx.MBeanProvider | 1 + .../store/berkeleydb/HAClusterManagementTest.java | 231 ++++++ .../store/berkeleydb/HAClusterTwoNodeTest.java | 217 ++++++ .../jmx/BDBHAMessageStoreManagerMBeanTest.java | 233 ++++++ .../server/store/berkeleydb/BDBHAMessageStore.java | 32 +- .../berkeleydb/BDBHAMessageStoreManagerMBean.java | 214 ------ .../server/store/berkeleydb/BDBMessageStore.java | 8 + .../store/berkeleydb/ManagedBDBHAMessageStore.java | 82 -- .../store/berkeleydb/upgrade/UpgradeFrom5To6.java | 45 +- .../BDBHAMessageStoreManagerMBeanTest.java | 225 ------ .../BDBMessageStoreConfigurationTest.java | 20 + .../store/berkeleydb/BDBMessageStoreTest.java | 2 +- .../store/berkeleydb/HAClusterManagementTest.java | 230 ------ .../store/berkeleydb/HAClusterTwoNodeTest.java | 216 ------ .../upgrade/AbstractUpgradeTestCase.java | 4 +- .../berkeleydb/upgrade/UpgradeFrom4to5Test.java | 44 +- .../berkeleydb/upgrade/UpgradeFrom5To6Test.java | 109 ++- .../upgrade/bdbstore-v4/test-store/00000000.jdb | Bin 1357197 -> 1361990 bytes .../upgrade/bdbstore-v5/test-store/00000000.jdb | Bin 1357227 -> 1361990 bytes .../upgrade/bdbstore-v5/test-store/00000001.jdb | Bin 1332881 -> 1333643 bytes .../java/broker-plugins/access-control/MANIFEST.MF | 2 - .../experimental/shutdown/MANIFEST.MF | 16 - .../broker-plugins/experimental/shutdown/build.xml | 31 - .../java/org/apache/qpid/shutdown/Activator.java | 57 -- .../java/org/apache/qpid/shutdown/Shutdown.java | 130 ---- .../org/apache/qpid/shutdown/ShutdownMBean.java | 57 -- .../shutdown/src/main/java/shutdown.bnd | 25 - qpid/java/broker-plugins/firewall/MANIFEST.MF | 2 - qpid/java/broker-plugins/jmx/MANIFEST.MF | 65 ++ qpid/java/broker-plugins/jmx/build.xml | 43 ++ .../apache/qpid/server/jmx/AMQManagedObject.java | 84 ++ .../qpid/server/jmx/DefaultManagedObject.java | 189 +++++ .../org/apache/qpid/server/jmx/JMXActivator.java | 136 ++++ .../apache/qpid/server/jmx/JMXConfiguration.java | 76 ++ .../qpid/server/jmx/JMXManagedObjectRegistry.java | 499 ++++++++++++ .../org/apache/qpid/server/jmx/JMXService.java | 189 +++++ .../apache/qpid/server/jmx/MBeanIntrospector.java | 400 ++++++++++ .../server/jmx/MBeanInvocationHandlerImpl.java | 382 ++++++++++ .../org/apache/qpid/server/jmx/MBeanProvider.java | 52 ++ .../org/apache/qpid/server/jmx/ManagedObject.java | 57 ++ .../qpid/server/jmx/ManagedObjectRegistry.java | 48 ++ .../mbeans/AbstractStatisticsGatheringMBean.java | 196 +++++ .../jmx/mbeans/ConfigurationManagementMBean.java | 56 ++ .../qpid/server/jmx/mbeans/ConnectionMBean.java | 183 +++++ .../qpid/server/jmx/mbeans/ExchangeMBean.java | 323 ++++++++ .../server/jmx/mbeans/LoggingManagementMBean.java | 832 ++++++++++++++++++++ .../apache/qpid/server/jmx/mbeans/MBeanUtils.java | 57 ++ .../apache/qpid/server/jmx/mbeans/QueueMBean.java | 663 ++++++++++++++++ .../server/jmx/mbeans/ServerInformationMBean.java | 93 +++ .../apache/qpid/server/jmx/mbeans/Shutdown.java | 137 ++++ .../qpid/server/jmx/mbeans/ShutdownMBean.java | 58 ++ .../server/jmx/mbeans/UserManagementMBean.java | 179 +++++ .../qpid/server/jmx/mbeans/VirtualHostMBean.java | 208 +++++ .../server/jmx/mbeans/VirtualHostManagerMBean.java | 240 ++++++ .../qpid/server/jmx/NoopManagedObjectRegistry.java | 46 ++ .../server/jmx/mbeans/ConnectionMBeanTest.java | 232 ++++++ .../jmx/mbeans/LoggingManagementMBeanTest.java | 434 +++++++++++ .../qpid/server/jmx/mbeans/MBeanTestUtils.java | 40 + .../qpid/server/jmx/mbeans/QueueMBeanTest.java | 368 +++++++++ .../server/jmx/mbeans/UserManagementMBeanTest.java | 157 ++++ .../jmx/mbeans/VirtualHostManagerMBeanTest.java | 175 +++++ .../management/jmx/BrokerManagementTest.java | 128 ++++ .../management/jmx/ConnectionManagementTest.java | 283 +++++++ .../management/jmx/ManagementActorLoggingTest.java | 480 ++++++++++++ .../management/jmx/ManagementLoggingTest.java | 317 ++++++++ .../management/jmx/QueueManagementTest.java | 609 +++++++++++++++ .../systest/management/jmx/StatisticsTest.java | 204 +++++ .../systest/management/jmx/UserManagementTest.java | 251 ++++++ .../UserManagementWithBase64MD5PasswordsTest.java | 39 + qpid/java/broker-plugins/management/MANIFEST.MF | 66 ++ qpid/java/broker-plugins/management/build.xml | 39 + .../qpid/server/management/plugin/Management.java | 155 ++++ .../management/plugin/ManagementActivator.java | 72 ++ .../management/plugin/ManagementConfiguration.java | 77 ++ .../plugin/servlet/DefinedFileServlet.java | 79 ++ .../management/plugin/servlet/FileServlet.java | 109 +++ .../plugin/servlet/api/ExchangesServlet.java | 208 +++++ .../plugin/servlet/api/VhostsServlet.java | 118 +++ .../plugin/servlet/rest/AbstractServlet.java | 215 ++++++ .../plugin/servlet/rest/KeyComparator.java | 53 ++ .../plugin/servlet/rest/LogRecordsServlet.java | 86 +++ .../plugin/servlet/rest/MapComparator.java | 74 ++ .../plugin/servlet/rest/MessageContentServlet.java | 177 +++++ .../plugin/servlet/rest/MessageServlet.java | 503 ++++++++++++ .../plugin/servlet/rest/RestServlet.java | 576 ++++++++++++++ .../plugin/servlet/rest/SaslServlet.java | 253 ++++++ .../plugin/servlet/rest/StructureServlet.java | 104 +++ .../src/main/java/resources/addBinding.html | 42 + .../src/main/java/resources/addExchange.html | 53 ++ .../src/main/java/resources/addQueue.html | 174 +++++ .../resources/authenticationprovider/addUser.html | 42 + .../authenticationprovider/setPassword.html | 42 + ...showPrincipalDatabaseAuthenticationManager.html | 29 + .../src/main/java/resources/css/common.css | 92 +++ .../management/src/main/java/resources/footer.html | 28 + .../java/resources/js/qpid/authorization/sasl.js | 213 ++++++ .../resources/js/qpid/common/UpdatableStore.js | 110 +++ .../main/java/resources/js/qpid/common/footer.js | 30 + .../java/resources/js/qpid/common/formatter.js | 99 +++ .../java/resources/js/qpid/common/properties.js | 26 + .../main/java/resources/js/qpid/common/updater.js | 45 ++ .../src/main/java/resources/js/qpid/common/util.js | 56 ++ .../js/qpid/management/AuthenticationProvider.js | 122 +++ .../java/resources/js/qpid/management/Broker.js | 205 +++++ .../resources/js/qpid/management/Connection.js | 213 ++++++ .../java/resources/js/qpid/management/Exchange.js | 229 ++++++ .../java/resources/js/qpid/management/Queue.js | 438 +++++++++++ .../resources/js/qpid/management/VirtualHost.js | 327 ++++++++ .../resources/js/qpid/management/addBinding.js | 223 ++++++ .../resources/js/qpid/management/addExchange.js | 147 ++++ .../java/resources/js/qpid/management/addQueue.js | 158 ++++ .../PrincipalDatabaseAuthenticationManager.js | 327 ++++++++ .../resources/js/qpid/management/controller.js | 104 +++ .../js/qpid/management/moveCopyMessages.js | 137 ++++ .../resources/js/qpid/management/showMessage.js | 142 ++++ .../java/resources/js/qpid/management/treeView.js | 313 ++++++++ .../src/main/java/resources/management.html | 92 +++ .../src/main/java/resources/moveCopyMessages.html | 36 + .../src/main/java/resources/showAuthProvider.html | 25 + .../src/main/java/resources/showBroker.html | 25 + .../src/main/java/resources/showConnection.html | 47 ++ .../src/main/java/resources/showExchange.html | 46 ++ .../src/main/java/resources/showMessage.html | 73 ++ .../src/main/java/resources/showQueue.html | 97 +++ .../src/main/java/resources/showVirtualHost.html | 84 ++ qpid/java/broker/etc/broker_example.acl | 19 + qpid/java/broker/etc/log4j.xml | 6 - qpid/java/broker/etc/passwd | 2 + .../apache/log4j/xml/QpidLog4JConfigurator.java | 74 -- .../org/apache/qpid/qmf/ManagementExchange.java | 10 + .../main/java/org/apache/qpid/qmf/QMFMessage.java | 18 + .../apache/qpid/server/AMQBrokerManagerMBean.java | 410 ---------- .../java/org/apache/qpid/server/AMQChannel.java | 15 +- .../main/java/org/apache/qpid/server/Broker.java | 35 +- .../java/org/apache/qpid/server/BrokerOptions.java | 20 +- .../qpid/server/configuration/ExchangeConfig.java | 5 + .../server/configuration/QueueConfiguration.java | 6 + .../server/configuration/ServerConfiguration.java | 52 +- .../management/ConfigurationManagementMBean.java | 47 -- .../qpid/server/connection/ConnectionRegistry.java | 15 + .../qpid/server/exchange/AbstractExchange.java | 57 +- .../server/exchange/AbstractExchangeMBean.java | 215 ------ .../server/exchange/DefaultExchangeRegistry.java | 45 +- .../qpid/server/exchange/DirectExchange.java | 5 - .../qpid/server/exchange/DirectExchangeMBean.java | 85 --- .../qpid/server/exchange/ExchangeRegistry.java | 14 +- .../qpid/server/exchange/FanoutExchange.java | 5 - .../qpid/server/exchange/FanoutExchangeMBean.java | 74 -- .../qpid/server/exchange/HeadersExchange.java | 5 - .../qpid/server/exchange/HeadersExchangeMBean.java | 158 ---- .../apache/qpid/server/exchange/TopicExchange.java | 5 - .../qpid/server/exchange/TopicExchangeMBean.java | 81 -- .../management/ServerInformationMBean.java | 145 ---- .../apache/qpid/server/logging/LogRecorder.java | 210 +++++ .../logging/management/LoggingManagementMBean.java | 822 -------------------- .../qpid/server/management/AMQManagedObject.java | 105 --- .../AbstractAMQManagedConnectionObject.java | 90 --- .../server/management/DefaultManagedObject.java | 192 ----- .../management/JMXManagedObjectRegistry.java | 490 ------------ .../qpid/server/management/MBeanIntrospector.java | 399 ---------- .../management/MBeanInvocationHandlerImpl.java | 367 --------- .../apache/qpid/server/management/Managable.java | 34 - .../qpid/server/management/ManagedObject.java | 59 -- .../server/management/ManagedObjectRegistry.java | 49 -- .../management/NoopManagedObjectRegistry.java | 58 -- .../qpid/server/message/AMQMessageHeader.java | 6 + .../server/message/ContentHeaderBodyAdapter.java | 18 + .../qpid/server/message/MessageMetaData.java | 17 + .../qpid/server/message/MessageMetaData_1_0.java | 28 +- .../qpid/server/message/MessageTransferHeader.java | 29 +- .../org/apache/qpid/server/model/Attribute.java | 199 +++++ .../qpid/server/model/AuthenticationMethod.java | 33 + .../qpid/server/model/AuthenticationProvider.java | 55 ++ .../java/org/apache/qpid/server/model/Broker.java | 82 ++ .../server/model/ConfigurationChangeListener.java | 3 +- .../apache/qpid/server/model/ConfiguredObject.java | 25 +- .../qpid/server/model/ConfiguredObjectFinder.java | 38 + .../org/apache/qpid/server/model/Connection.java | 107 +++ .../java/org/apache/qpid/server/model/Event.java | 27 + .../org/apache/qpid/server/model/EventType.java | 60 ++ .../org/apache/qpid/server/model/Exchange.java | 2 +- .../java/org/apache/qpid/server/model/Model.java | 97 +++ ...rdCredentialManagingAuthenticationProvider.java | 46 ++ .../java/org/apache/qpid/server/model/Port.java | 91 +++ .../org/apache/qpid/server/model/Protocol.java | 32 + .../java/org/apache/qpid/server/model/Queue.java | 6 +- .../server/model/QueueNotificationListener.java | 28 + .../java/org/apache/qpid/server/model/Session.java | 82 ++ .../org/apache/qpid/server/model/Statistics.java | 11 +- .../org/apache/qpid/server/model/Transport.java | 27 + .../apache/qpid/server/model/UUIDGenerator.java | 6 +- .../java/org/apache/qpid/server/model/User.java | 59 ++ .../org/apache/qpid/server/model/VirtualHost.java | 154 ++++ .../apache/qpid/server/model/VirtualHostAlias.java | 37 + .../qpid/server/model/adapter/AbstractAdapter.java | 293 +++++++ .../adapter/AuthenticationProviderAdapter.java | 487 ++++++++++++ .../qpid/server/model/adapter/BindingAdapter.java | 222 ++++++ .../qpid/server/model/adapter/BrokerAdapter.java | 484 ++++++++++++ .../server/model/adapter/ConnectionAdapter.java | 315 ++++++++ .../qpid/server/model/adapter/ConsumerAdapter.java | 225 ++++++ .../qpid/server/model/adapter/ExchangeAdapter.java | 409 ++++++++++ .../qpid/server/model/adapter/HTTPPortAdapter.java | 262 +++++++ .../qpid/server/model/adapter/NoStatistics.java | 46 ++ .../qpid/server/model/adapter/PortAdapter.java | 318 ++++++++ .../qpid/server/model/adapter/QueueAdapter.java | 702 +++++++++++++++++ .../qpid/server/model/adapter/SessionAdapter.java | 239 ++++++ .../server/model/adapter/StatisticsAdapter.java | 67 ++ .../server/model/adapter/VirtualHostAdapter.java | 844 +++++++++++++++++++++ .../model/adapter/VirtualHostAliasAdapter.java | 142 ++++ .../server/plugins/OsgiSystemPackages.properties | 41 +- .../qpid/server/protocol/AMQConnectionModel.java | 12 + .../qpid/server/protocol/AMQProtocolEngine.java | 97 +-- .../server/protocol/AMQProtocolSessionMBean.java | 344 --------- .../qpid/server/protocol/AMQSessionModel.java | 12 +- .../server/protocol/v1_0/Subscription_1_0.java | 43 ++ .../org/apache/qpid/server/queue/AMQQueue.java | 68 +- .../apache/qpid/server/queue/AMQQueueFactory.java | 69 +- .../apache/qpid/server/queue/AMQQueueMBean.java | 664 ---------------- .../qpid/server/queue/DefaultQueueRegistry.java | 50 +- .../qpid/server/queue/NotificationCheck.java | 39 +- .../apache/qpid/server/queue/QueueEntryImpl.java | 4 +- .../server/queue/QueueNotificationListener.java | 27 - .../apache/qpid/server/queue/QueueRegistry.java | 8 + .../apache/qpid/server/queue/SimpleAMQQueue.java | 456 +++++------ .../qpid/server/registry/ApplicationRegistry.java | 227 +++--- .../ConfigurationFileApplicationRegistry.java | 16 - .../qpid/server/registry/IApplicationRegistry.java | 34 +- .../AbstractPasswordFilePrincipalDatabase.java | 7 +- .../auth/management/AMQUserManagementMBean.java | 186 ----- .../manager/AnonymousAuthenticationManager.java | 2 +- .../manager/AuthenticationManagerRegistry.java | 24 +- .../manager/IAuthenticationManagerRegistry.java | 13 + .../PrincipalDatabaseAuthenticationManager.java | 49 +- .../auth/rmi/RMIPasswordAuthenticator.java | 27 +- .../qpid/server/stats/StatisticsCounter.java | 8 +- .../qpid/server/stats/StatisticsGatherer.java | 12 - .../server/store/ConfigurationRecoveryHandler.java | 2 +- .../qpid/server/store/ConfiguredObjectHelper.java | 38 +- .../qpid/server/store/MemoryMessageStore.java | 6 + .../org/apache/qpid/server/store/MessageStore.java | 2 + .../apache/qpid/server/store/NullMessageStore.java | 2 +- .../qpid/server/store/derby/DerbyMessageStore.java | 9 + .../AssignedSubscriptionMessageGroupManager.java | 4 +- .../DefinedGroupMessageGroupManager.java | 4 +- .../ExplicitAcceptDispositionChangeListener.java | 2 +- .../ImplicitAcceptDispositionChangeListener.java | 4 + .../qpid/server/subscription/Subscription.java | 12 + .../qpid/server/subscription/SubscriptionImpl.java | 72 +- .../server/subscription/Subscription_0_10.java | 53 +- .../apache/qpid/server/transport/QpidAcceptor.java | 49 +- .../qpid/server/transport/ServerConnection.java | 93 +-- .../server/transport/ServerConnectionDelegate.java | 7 +- .../server/transport/ServerConnectionMBean.java | 263 ------- .../qpid/server/transport/ServerSession.java | 11 +- .../server/transport/ServerSessionDelegate.java | 6 +- .../qpid/server/virtualhost/VirtualHost.java | 3 - .../VirtualHostConfigRecoveryHandler.java | 13 +- .../qpid/server/virtualhost/VirtualHostImpl.java | 128 +--- .../server/virtualhost/VirtualHostRegistry.java | 31 + .../log4j/xml/QpidLog4JConfiguratorTest.java | 395 ---------- .../qpid/server/AMQBrokerManagerMBeanTest.java | 194 ----- .../configuration/QueueConfigurationTest.java | 12 + .../configuration/ServerConfigurationTest.java | 20 +- .../qpid/server/exchange/ExchangeMBeanTest.java | 235 ------ .../qpid/server/exchange/HeadersBindingTest.java | 27 +- .../management/LoggingManagementMBeanTest.java | 428 ----------- .../management/AMQUserManagementMBeanTest.java | 153 ---- .../protocol/AMQProtocolSessionMBeanTest.java | 146 ---- .../qpid/server/queue/AMQQueueAlertTest.java | 348 --------- .../qpid/server/queue/AMQQueueMBeanTest.java | 546 ------------- .../org/apache/qpid/server/queue/MockAMQQueue.java | 113 +-- .../qpid/server/queue/NotificationCheckTest.java | 106 +++ .../qpid/server/queue/SimpleAMQQueueTest.java | 156 +--- .../auth/rmi/RMIPasswordAuthenticatorTest.java | 3 +- .../store/DurableConfigurationStoreTest.java | 76 +- .../apache/qpid/server/store/MessageStoreTest.java | 10 +- .../store/OperationalLoggingListenerTest.java | 6 + .../qpid/server/subscription/MockSubscription.java | 323 +++++++- .../transport/ServerConnectionMBeanTest.java | 231 ------ .../qpid/server/txn/MockStoreTransaction.java | 6 + .../qpid/server/util/TestApplicationRegistry.java | 13 + .../qpid/server/virtualhost/MockVirtualHost.java | 16 - qpid/java/build.deps | 18 +- qpid/java/build.xml | 2 +- qpid/java/common.xml | 4 +- qpid/java/ivy.retrieve.xml | 9 + .../management/common/mbeans/ManagedQueue.java | 44 +- .../common/mbeans/ServerInformation.java | 2 +- .../qpid/management/ui/ApplicationRegistry.java | 2 +- .../qpid/management/ui/views/ViewUtility.java | 35 +- .../qpid/management/ui/ManagementConsoleTest.java | 123 --- qpid/java/module.xml | 8 +- qpid/java/systests/build.xml | 2 +- .../java/systests/etc/config-systests-settings.xml | 3 + .../management/jmx/ManagedBrokerMBeanTest.java | 164 ---- .../management/jmx/ManagedConnectionMBeanTest.java | 278 ------- .../qpid/management/jmx/ManagedQueueMBeanTest.java | 345 --------- .../management/jmx/ManagementActorLoggingTest.java | 480 ------------ .../jmx/MessageConnectionStatisticsTest.java | 101 --- .../jmx/MessageStatisticsConfigurationTest.java | 162 ---- .../jmx/MessageStatisticsDeliveryTest.java | 109 --- .../jmx/MessageStatisticsReportingTest.java | 90 --- .../qpid/management/jmx/MessageStatisticsTest.java | 196 ----- .../management/jmx/MessageStatisticsTestCase.java | 128 ---- .../qpid/server/logging/ManagementLoggingTest.java | 316 -------- .../org/apache/qpid/server/queue/ModelTest.java | 2 +- .../server/security/acl/ExternalACLJMXTest.java | 3 +- .../qpid/server/stats/StatisticsReportingTest.java | 158 ++++ .../qpid/server/store/QuotaMessageStore.java | 6 + .../apache/qpid/server/store/SlowMessageStore.java | 7 +- .../BrokerClosesClientConnectionTest.java | 1 - .../org/apache/qpid/test/utils/JMXTestUtils.java | 89 ++- .../apache/qpid/test/utils/QpidBrokerTestCase.java | 6 +- qpid/java/test-profiles/CPPExcludes | 9 +- qpid/java/test-profiles/Java010Excludes | 5 + qpid/java/test-profiles/JavaPre010Excludes | 5 + qpid/java/test-profiles/JavaTransientExcludes | 3 + 323 files changed, 27035 insertions(+), 13293 deletions(-) create mode 100644 qpid/java/bdbstore/jmx/MANIFEST.MF create mode 100644 qpid/java/bdbstore/jmx/build.xml create mode 100644 qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java create mode 100644 qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java create mode 100644 qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java create mode 100644 qpid/java/bdbstore/jmx/src/main/resources/services/org.apache.qpid.server.jmx.MBeanProvider create mode 100644 qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java create mode 100644 qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java create mode 100644 qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java delete mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBean.java delete mode 100644 qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ManagedBDBHAMessageStore.java delete mode 100644 qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBeanTest.java delete mode 100644 qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java delete mode 100644 qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java delete mode 100644 qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF delete mode 100644 qpid/java/broker-plugins/experimental/shutdown/build.xml delete mode 100644 qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java delete mode 100644 qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java delete mode 100644 qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java delete mode 100755 qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd create mode 100644 qpid/java/broker-plugins/jmx/MANIFEST.MF create mode 100644 qpid/java/broker-plugins/jmx/build.xml create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java create mode 100644 qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java create mode 100644 qpid/java/broker-plugins/management/MANIFEST.MF create mode 100644 qpid/java/broker-plugins/management/build.xml create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/Management.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/addBinding.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/addExchange.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/addQueue.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/addUser.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/setPassword.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/css/common.css create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/footer.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/authorization/sasl.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/UpdatableStore.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/footer.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/formatter.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/properties.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/updater.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/util.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/AuthenticationProvider.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Broker.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Connection.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Exchange.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Queue.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/VirtualHost.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/addBinding.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/addExchange.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/addQueue.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/controller.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/moveCopyMessages.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/showMessage.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/treeView.js create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/management.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/moveCopyMessages.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showAuthProvider.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showBroker.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showConnection.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showExchange.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showMessage.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showQueue.html create mode 100644 qpid/java/broker-plugins/management/src/main/java/resources/showVirtualHost.html delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchangeMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchangeMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchangeMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/logging/LogRecorder.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Attribute.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationMethod.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFinder.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Connection.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Event.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/EventType.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/PasswordCredentialManagingAuthenticationProvider.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Protocol.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/QueueNotificationListener.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Session.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Transport.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHost.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHostAlias.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConnectionAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/HTTPPortAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/NoStatistics.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/StatisticsAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAliasAdapter.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/queue/NotificationCheckTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java delete mode 100644 qpid/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedBrokerMBeanTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedConnectionMBeanTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagedQueueMBeanTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageConnectionStatisticsTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsConfigurationTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsDeliveryTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsReportingTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTestCase.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/stats/StatisticsReportingTest.java (limited to 'qpid/java') diff --git a/qpid/java/bdbstore/build.xml b/qpid/java/bdbstore/build.xml index 7df048c691..7c305c7c2f 100644 --- a/qpid/java/bdbstore/build.xml +++ b/qpid/java/bdbstore/build.xml @@ -17,7 +17,7 @@ - under the License. --> - + diff --git a/qpid/java/bdbstore/jmx/MANIFEST.MF b/qpid/java/bdbstore/jmx/MANIFEST.MF new file mode 100644 index 0000000000..7046c4326d --- /dev/null +++ b/qpid/java/bdbstore/jmx/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Qpid Bdbstore-Plugins JMX +Bundle-SymbolicName: bdbstore-plugins-jmx +Bundle-Description: Bdbstore 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-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ClassPath: . +Fragment-Host: broker-plugins-jmx +Import-Package: org.apache.qpid, + org.apache.qpid.management.common.mbeans.annotations, + org.apache.qpid.server.model, + org.apache.qpid.server.virtualhost, + org.apache.qpid.server.store.berkeleydb, + org.apache.log4j;version=1.2.16, + javax.management, + javax.management.openmbean +Export-Package: org.apache.qpid.server.store.berkeleydb.jmx diff --git a/qpid/java/bdbstore/jmx/build.xml b/qpid/java/bdbstore/jmx/build.xml new file mode 100644 index 0000000000..2015b0cbb5 --- /dev/null +++ b/qpid/java/bdbstore/jmx/build.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java new file mode 100644 index 0000000000..455573f7bc --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBean.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.jmx; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +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 org.apache.log4j.Logger; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.jmx.AMQManagedObject; +import org.apache.qpid.server.jmx.ManagedObject; +import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore; + +/** + * Management mbean for BDB HA. + *

+ * At runtime, the classloader loading this clas must have visibility of the other Qpid JMX classes. This is + * currently arranged through OSGI using the fragment feature so that this bundle shares the + * same classloader as broker-plugins-jmx. See the Fragment-Host: header within the MANIFEST.MF + * of this bundle. + *

+ */ +public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements ManagedBDBHAMessageStore +{ + private static final Logger LOGGER = Logger.getLogger(BDBHAMessageStoreManagerMBean.class); + + private static final TabularType GROUP_MEMBERS_TABLE; + private static final CompositeType GROUP_MEMBER_ROW; + private static final OpenType[] GROUP_MEMBER_ATTRIBUTE_TYPES; + + static + { + try + { + GROUP_MEMBER_ATTRIBUTE_TYPES = new OpenType[] {SimpleType.STRING, SimpleType.STRING}; + final String[] itemNames = new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT}; + final String[] itemDescriptions = new String[] {"Unique node name", "Node host / port "}; + GROUP_MEMBER_ROW = new CompositeType("GroupMember", "Replication group member", + itemNames, + itemDescriptions, + GROUP_MEMBER_ATTRIBUTE_TYPES ); + GROUP_MEMBERS_TABLE = new TabularType("GroupMembers", "Replication group memebers", + GROUP_MEMBER_ROW, + new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME}); + } + catch (final OpenDataException ode) + { + throw new ExceptionInInitializerError(ode); + } + } + + private final BDBHAMessageStore _store; + + protected BDBHAMessageStoreManagerMBean(BDBHAMessageStore store, ManagedObject parent) throws JMException + { + super(ManagedBDBHAMessageStore.class, ManagedBDBHAMessageStore.TYPE, ((AMQManagedObject)parent).getRegistry()); + LOGGER.debug("Creating BDBHAMessageStoreManagerMBean"); + _store = store; + register(); + } + + @Override + public String getObjectInstanceName() + { + return _store.getName(); + } + + @Override + public String getGroupName() + { + return _store.getGroupName(); + } + + @Override + public String getNodeName() + { + return _store.getNodeName(); + } + + @Override + public String getNodeHostPort() + { + return _store.getNodeHostPort(); + } + + @Override + public String getHelperHostPort() + { + return _store.getHelperHostPort(); + } + + @Override + public String getDurability() throws IOException, JMException + { + try + { + return _store.getDurability(); + } + catch (RuntimeException e) + { + LOGGER.debug("Failed query replication policy", e); + throw new JMException(e.getMessage()); + } + } + + + @Override + public boolean getCoalescingSync() throws IOException, JMException + { + return _store.isCoalescingSync(); + } + + @Override + public String getNodeState() throws IOException, JMException + { + try + { + return _store.getNodeState(); + } + catch (RuntimeException e) + { + LOGGER.debug("Failed query node state", e); + throw new JMException(e.getMessage()); + } + } + + @Override + public boolean getDesignatedPrimary() throws IOException, JMException + { + try + { + return _store.isDesignatedPrimary(); + } + catch (RuntimeException e) + { + LOGGER.debug("Failed query designated primary", e); + throw new JMException(e.getMessage()); + } + } + + @Override + public TabularData getAllNodesInGroup() throws IOException, JMException + { + final TabularDataSupport data = new TabularDataSupport(GROUP_MEMBERS_TABLE); + final List> members = _store.getGroupMembers(); + + for (Map map : members) + { + CompositeData memberData = new CompositeDataSupport(GROUP_MEMBER_ROW, map); + data.put(memberData); + } + return data; + } + + @Override + public void removeNodeFromGroup(String nodeName) throws JMException + { + try + { + _store.removeNodeFromGroup(nodeName); + } + catch (AMQStoreException e) + { + LOGGER.error("Failed to remove node " + nodeName + " from group", e); + throw new JMException(e.getMessage()); + } + } + + @Override + public void setDesignatedPrimary(boolean primary) throws JMException + { + try + { + _store.setDesignatedPrimary(primary); + } + catch (AMQStoreException e) + { + LOGGER.error("Failed to set node " + _store.getNodeName() + " as designated primary", e); + throw new JMException(e.getMessage()); + } + } + + @Override + public void updateAddress(String nodeName, String newHostName, int newPort) throws JMException + { + try + { + _store.updateAddress(nodeName, newHostName, newPort); + } + catch(AMQStoreException e) + { + LOGGER.error("Failed to update address for node " + nodeName + " to " + newHostName + ":" + newPort, e); + throw new JMException(e.getMessage()); + } + } + + @Override + public ManagedObject getParentObject() + { + return null; + } + +} diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.java new file mode 100644 index 0000000000..837da1eef3 --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanProvider.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.store.berkeleydb.jmx; + +import javax.management.JMException; +import javax.management.StandardMBean; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.jmx.MBeanProvider; +import org.apache.qpid.server.jmx.ManagedObject; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +/** + * This provide will create a {@link BDBHAMessageStoreManagerMBean} if the child is a virtual + * host and of type {@link BDBHAMessageStore#BDB_HA_STORE_TYPE}. + * + */ +public class BDBHAMessageStoreManagerMBeanProvider implements MBeanProvider +{ + private static final Logger LOGGER = Logger.getLogger(BDBHAMessageStoreManagerMBeanProvider.class); + + public BDBHAMessageStoreManagerMBeanProvider() + { + super(); + } + + @Override + public boolean isChildManageableByMBean(ConfiguredObject child) + { + return (child instanceof VirtualHost + && BDBHAMessageStore.BDB_HA_STORE_TYPE.equals(child.getAttribute(VirtualHost.STORE_TYPE))); + } + + @Override + public StandardMBean createMBean(ConfiguredObject child, StandardMBean parent) throws JMException + { + VirtualHost virtualHostChild = (VirtualHost) child; + + VirtualHostRegistry virtualHostRegistry = ApplicationRegistry.getInstance().getVirtualHostRegistry(); + org.apache.qpid.server.virtualhost.VirtualHost vhost = virtualHostRegistry.getVirtualHost(virtualHostChild.getName()); + + BDBHAMessageStore messageStore = (BDBHAMessageStore) vhost.getMessageStore(); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Creating mBean for child " + child); + } + + return new BDBHAMessageStoreManagerMBean(messageStore, (ManagedObject) parent); + } +} diff --git a/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java new file mode 100644 index 0000000000..b85e44526b --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/main/java/org/apache/qpid/server/store/berkeleydb/jmx/ManagedBDBHAMessageStore.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.jmx; + +import java.io.IOException; + +import javax.management.JMException; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; + +public interface ManagedBDBHAMessageStore +{ + public static final String TYPE = "BDBHAMessageStore"; + + public static final String ATTR_GROUP_NAME = "GroupName"; + public static final String ATTR_NODE_NAME = "NodeName"; + public static final String ATTR_NODE_HOST_PORT = "NodeHostPort"; + public static final String ATTR_HELPER_HOST_PORT = "HelperHostPort"; + public static final String ATTR_DURABILITY = "Durability"; + public static final String ATTR_NODE_STATE = "NodeState"; + public static final String ATTR_DESIGNATED_PRIMARY = "DesignatedPrimary"; + public static final String ATTR_COALESCING_SYNC = "CoalescingSync"; + + @MBeanAttribute(name=ATTR_GROUP_NAME, description="Name identifying the group") + String getGroupName() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_NODE_NAME, description="Unique name identifying the node within the group") + String getNodeName() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_NODE_HOST_PORT, description="Host/port used to replicate data between this node and others in the group") + String getNodeHostPort() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_NODE_STATE, description="Current state of this node") + String getNodeState() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_HELPER_HOST_PORT, description="Host/port used to allow a new node to discover other group members") + String getHelperHostPort() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_DURABILITY, description="Durability") + String getDurability() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_DESIGNATED_PRIMARY, description="Designated primary flag. Applicable to the two node case.") + boolean getDesignatedPrimary() throws IOException, JMException; + + @MBeanAttribute(name=ATTR_COALESCING_SYNC, description="Coalescing sync flag. Applicable to the master sync policies NO_SYNC and WRITE_NO_SYNC only.") + boolean getCoalescingSync() throws IOException, JMException; + + @MBeanAttribute(name="getAllNodesInGroup", description="Get all nodes within the group, regardless of whether currently attached or not") + TabularData getAllNodesInGroup() throws IOException, JMException; + + @MBeanOperation(name="removeNodeFromGroup", description="Remove an existing node from the group") + void removeNodeFromGroup(@MBeanOperationParameter(name="nodeName", description="name of node")String nodeName) throws JMException; + + @MBeanOperation(name="setDesignatedPrimary", description="Set/unset this node as the designated primary for the group. Applicable to the two node case.") + void setDesignatedPrimary(@MBeanOperationParameter(name="primary", description="designated primary")boolean primary) throws JMException; + + @MBeanOperation(name="updateAddress", description="Update the address of another node. The node must be in a STOPPED state.") + void updateAddress(@MBeanOperationParameter(name="nodeName", description="name of node")String nodeName, + @MBeanOperationParameter(name="newHostName", description="new hostname")String newHostName, + @MBeanOperationParameter(name="newPort", description="new port number")int newPort) throws JMException; +} + diff --git a/qpid/java/bdbstore/jmx/src/main/resources/services/org.apache.qpid.server.jmx.MBeanProvider b/qpid/java/bdbstore/jmx/src/main/resources/services/org.apache.qpid.server.jmx.MBeanProvider new file mode 100644 index 0000000000..b5bc947612 --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/main/resources/services/org.apache.qpid.server.jmx.MBeanProvider @@ -0,0 +1 @@ +org.apache.qpid.server.store.berkeleydb.jmx.BDBHAMessageStoreManagerMBeanProvider diff --git a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java new file mode 100644 index 0000000000..45038bf050 --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb; + +import static com.sleepycat.je.rep.ReplicatedEnvironment.State.DETACHED; +import static com.sleepycat.je.rep.ReplicatedEnvironment.State.MASTER; +import static com.sleepycat.je.rep.ReplicatedEnvironment.State.REPLICA; +import static com.sleepycat.je.rep.ReplicatedEnvironment.State.UNKNOWN; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.jms.Connection; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import org.apache.log4j.Logger; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +import com.sleepycat.je.EnvironmentFailureException; + +/** + * System test verifying the ability to control a cluster via the Management API. + * + * @see HAClusterBlackboxTest + */ +public class HAClusterManagementTest extends QpidBrokerTestCase +{ + protected static final Logger LOGGER = Logger.getLogger(HAClusterManagementTest.class); + + private static final Set NON_MASTER_STATES = new HashSet(Arrays.asList(REPLICA.toString(), DETACHED.toString(), UNKNOWN.toString()));; + private static final String VIRTUAL_HOST = "test"; + + private static final String MANAGED_OBJECT_QUERY = "org.apache.qpid:type=BDBHAMessageStore,name=" + VIRTUAL_HOST; + private static final int NUMBER_OF_NODES = 4; + + private final HATestClusterCreator _clusterCreator = new HATestClusterCreator(this, VIRTUAL_HOST, NUMBER_OF_NODES); + private final JMXTestUtils _jmxUtils = new JMXTestUtils(this); + + private ConnectionURL _brokerFailoverUrl; + + @Override + protected void setUp() throws Exception + { + _brokerType = BrokerType.SPAWNED; + _jmxUtils.setUp(); + + _clusterCreator.configureClusterNodes(); + _brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes(); + _clusterCreator.startCluster(); + + super.setUp(); + } + + @Override + protected void tearDown() throws Exception + { + try + { + _jmxUtils.close(); + } + finally + { + super.tearDown(); + } + } + + @Override + public void startBroker() throws Exception + { + // Don't start default broker provided by QBTC. + } + + public void testReadonlyMBeanAttributes() throws Exception + { + final int brokerPortNumber = getBrokerPortNumbers().iterator().next(); + final int bdbPortNumber = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumber); + + ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumber); + assertEquals("Unexpected store group name", _clusterCreator.getGroupName(), storeBean.getGroupName()); + assertEquals("Unexpected store node name", _clusterCreator.getNodeNameForNodeAt(bdbPortNumber), storeBean.getNodeName()); + assertEquals("Unexpected store node host port",_clusterCreator.getNodeHostPortForNodeAt(bdbPortNumber), storeBean.getNodeHostPort()); + assertEquals("Unexpected store helper host port", _clusterCreator.getHelperHostPort(), storeBean.getHelperHostPort()); + // As we have chosen an arbitrary broker from the cluster, we cannot predict its state + assertNotNull("Store state must not be null", storeBean.getNodeState()); + } + + public void testStateOfActiveBrokerIsMaster() throws Exception + { + final Connection activeConnection = getConnection(_brokerFailoverUrl); + final int activeBrokerPortNumber = _clusterCreator.getBrokerPortNumberFromConnection(activeConnection); + + ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(activeBrokerPortNumber); + assertEquals("Unexpected store state", MASTER.toString(), storeBean.getNodeState()); + } + + public void testStateOfNonActiveBrokerIsNotMaster() throws Exception + { + final Connection activeConnection = getConnection(_brokerFailoverUrl); + final int inactiveBrokerPortNumber = _clusterCreator.getPortNumberOfAnInactiveBroker(activeConnection); + ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(inactiveBrokerPortNumber); + final String nodeState = storeBean.getNodeState(); + assertTrue("Unexpected store state : " + nodeState, NON_MASTER_STATES.contains(nodeState)); + } + + public void testGroupMembers() throws Exception + { + final int brokerPortNumber = getBrokerPortNumbers().iterator().next(); + + ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumber); + final TabularData groupMembers = storeBean.getAllNodesInGroup(); + assertNotNull(groupMembers); + + final int numberOfDataRows = groupMembers.size(); + assertEquals("Unexpected number of data rows", NUMBER_OF_NODES ,numberOfDataRows); + + for(int bdbPortNumber : _clusterCreator.getBdbPortNumbers()) + { + final String nodeName = _clusterCreator.getNodeNameForNodeAt(bdbPortNumber); + final String nodeHostPort = _clusterCreator.getNodeHostPortForNodeAt(bdbPortNumber); + + CompositeData row = groupMembers.get(new Object[] {nodeName}); + assertNotNull("Table does not contain row for node name " + nodeName, row); + assertEquals(nodeHostPort, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT)); + } + } + + public void testRemoveNodeFromGroup() throws Exception + { + final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); + final int brokerPortNumberToMakeObservation = brokerPortNumberIterator.next(); + final int brokerPortNumberToBeRemoved = brokerPortNumberIterator.next(); + final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToMakeObservation); + final int numberOfDataRows = storeBean.getAllNodesInGroup().size(); + assertEquals("Unexpected number of data rows before test", NUMBER_OF_NODES ,numberOfDataRows); + + final String removedNodeName = _clusterCreator.getNodeNameForNodeAt(_clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeRemoved)); + _clusterCreator.stopNode(brokerPortNumberToBeRemoved); + storeBean.removeNodeFromGroup(removedNodeName); + + final int numberOfDataRowsAfterRemoval = storeBean.getAllNodesInGroup().size(); + assertEquals("Unexpected number of data rows before test", NUMBER_OF_NODES - 1,numberOfDataRowsAfterRemoval); + } + + /** + * Updates the address of a node. + * + * If the broker (node) can subsequently start without error then the update was a success, hence no need for an explicit + * assert. + * + * @see #testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() for converse case + */ + public void testUpdateAddress() throws Exception + { + final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); + final int brokerPortNumberToPerformUpdate = brokerPortNumberIterator.next(); + final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next(); + final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToPerformUpdate); + + _clusterCreator.stopNode(brokerPortNumberToBeMoved); + + final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved); + final int newBdbPort = getNextAvailable(oldBdbPort + 1); + + storeBean.updateAddress(_clusterCreator.getNodeNameForNodeAt(oldBdbPort), _clusterCreator.getIpAddressOfBrokerHost(), newBdbPort); + + _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort); + + _clusterCreator.startNode(brokerPortNumberToBeMoved); + } + + /** + * @see #testUpdateAddress() + */ + public void testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() throws Exception + { + final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); + final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next(); + + _clusterCreator.stopNode(brokerPortNumberToBeMoved); + + final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved); + final int newBdbPort = getNextAvailable(oldBdbPort + 1); + + // now deliberately don't call updateAddress + + _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort); + + try + { + _clusterCreator.startNode(brokerPortNumberToBeMoved); + fail("Exception not thrown"); + } + catch(RuntimeException rte) + { + //check cause was BDBs EnvironmentFailureException + assertTrue(rte.getMessage().contains(EnvironmentFailureException.class.getName())); + // PASS + } + } + + private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort( + final int activeBrokerPortNumber) throws Exception + { + _jmxUtils.open(activeBrokerPortNumber); + + return _jmxUtils.getManagedObject(ManagedBDBHAMessageStore.class, MANAGED_OBJECT_QUERY); + } +} diff --git a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java new file mode 100644 index 0000000000..22877ec36c --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb; + +import java.io.File; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; + +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +import com.sleepycat.je.rep.ReplicationConfig; + +public class HAClusterTwoNodeTest extends QpidBrokerTestCase +{ + private static final long RECEIVE_TIMEOUT = 5000l; + + private static final String VIRTUAL_HOST = "test"; + + private static final String MANAGED_OBJECT_QUERY = "org.apache.qpid:type=BDBHAMessageStore,name=" + VIRTUAL_HOST; + private static final int NUMBER_OF_NODES = 2; + + private final HATestClusterCreator _clusterCreator = new HATestClusterCreator(this, VIRTUAL_HOST, NUMBER_OF_NODES); + private final JMXTestUtils _jmxUtils = new JMXTestUtils(this); + + private ConnectionURL _brokerFailoverUrl; + + @Override + protected void setUp() throws Exception + { + _brokerType = BrokerType.SPAWNED; + + assertTrue(isJavaBroker()); + assertTrue(isBrokerStorePersistent()); + _jmxUtils.setUp(); + + super.setUp(); + } + + @Override + protected void tearDown() throws Exception + { + try + { + _jmxUtils.close(); + } + finally + { + super.tearDown(); + } + } + + @Override + public void startBroker() throws Exception + { + // Don't start default broker provided by QBTC. + } + + private void startCluster(boolean designedPrimary) throws Exception + { + setSystemProperty("java.util.logging.config.file", "etc" + File.separator + "log.properties"); + + String storeConfigKeyPrefix = _clusterCreator.getStoreConfigKeyPrefix(); + + setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).name", ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT); + setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).value", "2 s"); + + setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).name", ReplicationConfig.ELECTIONS_PRIMARY_RETRIES); + setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).value", "0"); + + _clusterCreator.configureClusterNodes(); + _clusterCreator.setDesignatedPrimaryOnFirstBroker(designedPrimary); + _brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes(); + _clusterCreator.startCluster(); + } + + public void testMasterDesignatedPrimaryCanBeRestartedWithoutReplica() throws Exception + { + startCluster(true); + final Connection initialConnection = getConnection(_brokerFailoverUrl); + int masterPort = _clusterCreator.getBrokerPortNumberFromConnection(initialConnection); + assertProducingConsuming(initialConnection); + initialConnection.close(); + _clusterCreator.stopCluster(); + _clusterCreator.startNode(masterPort); + final Connection secondConnection = getConnection(_brokerFailoverUrl); + assertProducingConsuming(secondConnection); + secondConnection.close(); + } + + public void testClusterRestartWithoutDesignatedPrimary() throws Exception + { + startCluster(false); + final Connection initialConnection = getConnection(_brokerFailoverUrl); + assertProducingConsuming(initialConnection); + initialConnection.close(); + _clusterCreator.stopCluster(); + _clusterCreator.startClusterParallel(); + final Connection secondConnection = getConnection(_brokerFailoverUrl); + assertProducingConsuming(secondConnection); + secondConnection.close(); + } + + public void testDesignatedPrimaryContinuesAfterSecondaryStopped() throws Exception + { + startCluster(true); + _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); + final Connection connection = getConnection(_brokerFailoverUrl); + assertNotNull("Expected to get a valid connection to primary", connection); + assertProducingConsuming(connection); + } + + public void testPersistentOperationsFailOnNonDesignatedPrimarysAfterSecondaryStopped() throws Exception + { + startCluster(false); + _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); + final Connection connection = getConnection(_brokerFailoverUrl); + assertNotNull("Expected to get a valid connection to primary", connection); + try + { + assertProducingConsuming(connection); + fail("JMS peristent operations succeded on Master 'not designated primary' buy they should fail as replica is not available"); + } + catch(JMSException e) + { + // JMSException should be thrown on transaction start/commit + } + } + + public void testSecondaryDoesNotBecomePrimaryWhenDesignatedPrimaryStopped() throws Exception + { + startCluster(true); + _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfPrimary()); + + try + { + getConnection(_brokerFailoverUrl); + fail("Connection not expected"); + } + catch (JMSException e) + { + // PASS + } + } + + public void testInitialDesignatedPrimaryStateOfNodes() throws Exception + { + startCluster(true); + final ManagedBDBHAMessageStore primaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfPrimary()); + assertTrue("Expected primary node to be set as designated primary", primaryStoreBean.getDesignatedPrimary()); + + final ManagedBDBHAMessageStore secondaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); + assertFalse("Expected secondary node to NOT be set as designated primary", secondaryStoreBean.getDesignatedPrimary()); + } + + public void testSecondaryDesignatedAsPrimaryAfterOrginalPrimaryStopped() throws Exception + { + startCluster(true); + _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfPrimary()); + final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); + + assertFalse("Expected node to NOT be set as designated primary", storeBean.getDesignatedPrimary()); + storeBean.setDesignatedPrimary(true); + assertTrue("Expected node to now be set as designated primary", storeBean.getDesignatedPrimary()); + + final Connection connection = getConnection(_brokerFailoverUrl); + assertNotNull("Expected to get a valid connection to new primary", connection); + assertProducingConsuming(connection); + } + + private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort( + final int activeBrokerPortNumber) throws Exception + { + _jmxUtils.open(activeBrokerPortNumber); + + ManagedBDBHAMessageStore storeBean = _jmxUtils.getManagedObject(ManagedBDBHAMessageStore.class, MANAGED_OBJECT_QUERY); + return storeBean; + } + + private void assertProducingConsuming(final Connection connection) throws JMSException, Exception + { + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + Destination destination = session.createQueue(getTestQueueName()); + MessageConsumer consumer = session.createConsumer(destination); + sendMessage(session, destination, 1); + connection.start(); + Message m1 = consumer.receive(RECEIVE_TIMEOUT); + assertNotNull("Message 1 is not received", m1); + assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX)); + session.commit(); + } + +} diff --git a/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java new file mode 100644 index 0000000000..49b3ddd3dc --- /dev/null +++ b/qpid/java/bdbstore/jmx/src/test/java/org/apache/qpid/server/store/berkeleydb/jmx/BDBHAMessageStoreManagerMBeanTest.java @@ -0,0 +1,233 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.jmx; + +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.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.TabularData; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.jmx.AMQManagedObject; +import org.apache.qpid.server.jmx.ManagedObjectRegistry; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore; +import org.apache.qpid.server.store.berkeleydb.jmx.BDBHAMessageStoreManagerMBean; +import org.apache.qpid.server.store.berkeleydb.jmx.ManagedBDBHAMessageStore; + +public class BDBHAMessageStoreManagerMBeanTest extends TestCase +{ + private static final String TEST_GROUP_NAME = "testGroupName"; + private static final String TEST_NODE_NAME = "testNodeName"; + private static final String TEST_NODE_HOST_PORT = "host:1234"; + private static final String TEST_HELPER_HOST_PORT = "host:5678"; + private static final String TEST_DURABILITY = "sync,sync,all"; + private static final String TEST_NODE_STATE = "MASTER"; + private static final String TEST_STORE_NAME = "testStoreName"; + private static final boolean TEST_DESIGNATED_PRIMARY_FLAG = false; + + private BDBHAMessageStore _store; + private BDBHAMessageStoreManagerMBean _mBean; + private AMQManagedObject _mBeanParent; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); + _store = mock(BDBHAMessageStore.class); + _mBeanParent = mock(AMQManagedObject.class); + when(_mBeanParent.getRegistry()).thenReturn(mock(ManagedObjectRegistry.class)); + _mBean = new BDBHAMessageStoreManagerMBean(_store, _mBeanParent); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + CurrentActor.remove(); + } + + public void testObjectName() throws Exception + { + when(_store.getName()).thenReturn(TEST_STORE_NAME); + + String expectedObjectName = "org.apache.qpid:type=BDBHAMessageStore,name=" + TEST_STORE_NAME; + assertEquals(expectedObjectName, _mBean.getObjectName().toString()); + } + + public void testGroupName() throws Exception + { + when(_store.getGroupName()).thenReturn(TEST_GROUP_NAME); + + assertEquals(TEST_GROUP_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_GROUP_NAME)); + } + + public void testNodeName() throws Exception + { + when(_store.getNodeName()).thenReturn(TEST_NODE_NAME); + + assertEquals(TEST_NODE_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_NAME)); + } + + public void testNodeHostPort() throws Exception + { + when(_store.getNodeHostPort()).thenReturn(TEST_NODE_HOST_PORT); + + assertEquals(TEST_NODE_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_HOST_PORT)); + } + + public void testHelperHostPort() throws Exception + { + when(_store.getHelperHostPort()).thenReturn(TEST_HELPER_HOST_PORT); + + assertEquals(TEST_HELPER_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_HELPER_HOST_PORT)); + } + + public void testDurability() throws Exception + { + when(_store.getDurability()).thenReturn(TEST_DURABILITY); + + assertEquals(TEST_DURABILITY, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DURABILITY)); + } + + public void testCoalescingSync() throws Exception + { + when(_store.isCoalescingSync()).thenReturn(true); + + assertEquals(true, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_COALESCING_SYNC)); + } + + public void testNodeState() throws Exception + { + when(_store.getNodeState()).thenReturn(TEST_NODE_STATE); + + assertEquals(TEST_NODE_STATE, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_STATE)); + } + + public void testDesignatedPrimaryFlag() throws Exception + { + when(_store.isDesignatedPrimary()).thenReturn(TEST_DESIGNATED_PRIMARY_FLAG); + + assertEquals(TEST_DESIGNATED_PRIMARY_FLAG, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DESIGNATED_PRIMARY)); + } + + public void testGroupMembersForGroupWithOneNode() throws Exception + { + List> members = Collections.singletonList(createTestNodeResult()); + when(_store.getGroupMembers()).thenReturn(members); + + final TabularData resultsTable = _mBean.getAllNodesInGroup(); + + assertTableHasHeadingsNamed(resultsTable, BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT); + + final int numberOfDataRows = resultsTable.size(); + assertEquals("Unexpected number of data rows", 1 ,numberOfDataRows); + final CompositeData row = (CompositeData) resultsTable.values().iterator().next(); + assertEquals(TEST_NODE_NAME, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME)); + assertEquals(TEST_NODE_HOST_PORT, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT)); + } + + public void testRemoveNodeFromReplicationGroup() throws Exception + { + _mBean.removeNodeFromGroup(TEST_NODE_NAME); + + verify(_store).removeNodeFromGroup(TEST_NODE_NAME); + } + + public void testRemoveNodeFromReplicationGroupWithError() throws Exception + { + doThrow(new AMQStoreException("mocked exception")).when(_store).removeNodeFromGroup(TEST_NODE_NAME); + + try + { + _mBean.removeNodeFromGroup(TEST_NODE_NAME); + fail("Exception not thrown"); + } + catch (JMException je) + { + // PASS + } + } + + public void testSetAsDesignatedPrimary() throws Exception + { + _mBean.setDesignatedPrimary(true); + + verify(_store).setDesignatedPrimary(true); + } + + public void testSetAsDesignatedPrimaryWithError() throws Exception + { + doThrow(new AMQStoreException("mocked exception")).when(_store).setDesignatedPrimary(true); + + try + { + _mBean.setDesignatedPrimary(true); + fail("Exception not thrown"); + } + catch (JMException je) + { + // PASS + } + } + + public void testUpdateAddress() throws Exception + { + String newHostName = "newHostName"; + int newPort = 1967; + + _mBean.updateAddress(TEST_NODE_NAME, newHostName, newPort); + + verify(_store).updateAddress(TEST_NODE_NAME, newHostName, newPort); + } + + private void assertTableHasHeadingsNamed(final TabularData resultsTable, String... headingNames) + { + CompositeType headingsRow = resultsTable.getTabularType().getRowType(); + for (final String headingName : headingNames) + { + assertTrue("Table should have column with heading " + headingName, headingsRow.containsKey(headingName)); + } + } + + private Map createTestNodeResult() + { + Map items = new HashMap(); + items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, TEST_NODE_NAME); + items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT, TEST_NODE_HOST_PORT); + return items; + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java index ab54d7d16a..c40f24dbc3 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStore.java @@ -105,6 +105,8 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess put(ReplicationConfig.LOG_FLUSH_TASK_INTERVAL, "1 min"); }}); + public static final String BDB_HA_STORE_TYPE = "BDB-HA"; + private String _groupName; private String _nodeName; private String _nodeHostPort; @@ -113,8 +115,6 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess private String _name; - private BDBHAMessageStoreManagerMBean _managedObject; - private CommitThreadWrapper _commitThreadWrapper; private boolean _coalescingSync; private boolean _designatedPrimary; @@ -149,8 +149,6 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess throw new ConfigurationException("Coalescing sync cannot be used with master sync policy " + SyncPolicy.SYNC + "! Please set highAvailability.coalescingSync to false in store configuration."); } - _managedObject = new BDBHAMessageStoreManagerMBean(this); - _managedObject.register(); super.configure(name, storeConfig); } @@ -394,28 +392,18 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess @Override protected void closeInternal() throws Exception { + substituteNoOpStateChangeListenerOn(getReplicatedEnvironment()); + try { - substituteNoOpStateChangeListenerOn(getReplicatedEnvironment()); - - try - { - if(_coalescingSync) - { - _commitThreadWrapper.stopCommitThread(); - } - } - finally + if(_coalescingSync) { - super.closeInternal(); + _commitThreadWrapper.stopCommitThread(); } } finally { - if (_managedObject != null) - { - _managedObject.unregister(); - } + super.closeInternal(); } } @@ -610,4 +598,10 @@ public class BDBHAMessageStore extends AbstractBDBMessageStore implements HAMess { } } + + @Override + public String getStoreType() + { + return BDB_HA_STORE_TYPE; + } } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBean.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBean.java deleted file mode 100644 index c2c7bf4c86..0000000000 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBean.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.server.store.berkeleydb; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import javax.management.JMException; -import javax.management.NotCompliantMBeanException; -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.log4j.Logger; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.management.AMQManagedObject; - -public class BDBHAMessageStoreManagerMBean extends AMQManagedObject implements ManagedBDBHAMessageStore -{ - private static final Logger LOGGER = Logger.getLogger(BDBHAMessageStoreManagerMBean.class); - - private static final TabularType GROUP_MEMBERS_TABLE; - private static final CompositeType GROUP_MEMBER_ROW; - private static final OpenType[] GROUP_MEMBER_ATTRIBUTE_TYPES; - - static - { - try - { - GROUP_MEMBER_ATTRIBUTE_TYPES = new OpenType[] {SimpleType.STRING, SimpleType.STRING}; - final String[] itemNames = new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT}; - final String[] itemDescriptions = new String[] {"Unique node name", "Node host / port "}; - GROUP_MEMBER_ROW = new CompositeType("GroupMember", "Replication group member", - itemNames, - itemDescriptions, - GROUP_MEMBER_ATTRIBUTE_TYPES ); - GROUP_MEMBERS_TABLE = new TabularType("GroupMembers", "Replication group memebers", - GROUP_MEMBER_ROW, - new String[] {BDBHAMessageStore.GRP_MEM_COL_NODE_NAME}); - } - catch (final OpenDataException ode) - { - throw new ExceptionInInitializerError(ode); - } - } - - private final BDBHAMessageStore _store; - - protected BDBHAMessageStoreManagerMBean(BDBHAMessageStore store) throws NotCompliantMBeanException - { - super(ManagedBDBHAMessageStore.class, ManagedBDBHAMessageStore.TYPE); - _store = store; - } - - @Override - public String getObjectInstanceName() - { - return _store.getName(); - } - - @Override - public String getGroupName() - { - return _store.getGroupName(); - } - - @Override - public String getNodeName() - { - return _store.getNodeName(); - } - - @Override - public String getNodeHostPort() - { - return _store.getNodeHostPort(); - } - - @Override - public String getHelperHostPort() - { - return _store.getHelperHostPort(); - } - - @Override - public String getDurability() throws IOException, JMException - { - try - { - return _store.getDurability(); - } - catch (RuntimeException e) - { - LOGGER.debug("Failed query replication policy", e); - throw new JMException(e.getMessage()); - } - } - - - @Override - public boolean getCoalescingSync() throws IOException, JMException - { - return _store.isCoalescingSync(); - } - - @Override - public String getNodeState() throws IOException, JMException - { - try - { - return _store.getNodeState(); - } - catch (RuntimeException e) - { - LOGGER.debug("Failed query node state", e); - throw new JMException(e.getMessage()); - } - } - - @Override - public boolean getDesignatedPrimary() throws IOException, JMException - { - try - { - return _store.isDesignatedPrimary(); - } - catch (RuntimeException e) - { - LOGGER.debug("Failed query designated primary", e); - throw new JMException(e.getMessage()); - } - } - - @Override - public TabularData getAllNodesInGroup() throws IOException, JMException - { - final TabularDataSupport data = new TabularDataSupport(GROUP_MEMBERS_TABLE); - final List> members = _store.getGroupMembers(); - - for (Map map : members) - { - CompositeData memberData = new CompositeDataSupport(GROUP_MEMBER_ROW, map); - data.put(memberData); - } - return data; - } - - @Override - public void removeNodeFromGroup(String nodeName) throws JMException - { - try - { - _store.removeNodeFromGroup(nodeName); - } - catch (AMQStoreException e) - { - LOGGER.error("Failed to remove node " + nodeName + " from group", e); - throw new JMException(e.getMessage()); - } - } - - @Override - public void setDesignatedPrimary(boolean primary) throws JMException - { - try - { - _store.setDesignatedPrimary(primary); - } - catch (AMQStoreException e) - { - LOGGER.error("Failed to set node " + _store.getNodeName() + " as designated primary", e); - throw new JMException(e.getMessage()); - } - } - - @Override - public void updateAddress(String nodeName, String newHostName, int newPort) throws JMException - { - try - { - _store.updateAddress(nodeName, newHostName, newPort); - } - catch(AMQStoreException e) - { - LOGGER.error("Failed to update address for node " + nodeName + " to " + newHostName + ":" + newPort, e); - throw new JMException(e.getMessage()); - } - } - -} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java index d5bf5374bc..82bc3d8564 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java @@ -42,6 +42,7 @@ import com.sleepycat.je.EnvironmentConfig; public class BDBMessageStore extends AbstractBDBMessageStore { private static final Logger LOGGER = Logger.getLogger(BDBMessageStore.class); + private static final String BDB_STORE_TYPE = "BDB"; private CommitThreadWrapper _commitThreadWrapper; @Override @@ -103,4 +104,11 @@ public class BDBMessageStore extends AbstractBDBMessageStore return _commitThreadWrapper.commit(tx, syncCommit); } + + @Override + public String getStoreType() + { + return BDB_STORE_TYPE; + } + } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ManagedBDBHAMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ManagedBDBHAMessageStore.java deleted file mode 100644 index 6499ea04e0..0000000000 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/ManagedBDBHAMessageStore.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store.berkeleydb; - -import java.io.IOException; - -import javax.management.JMException; -import javax.management.openmbean.TabularData; - -import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute; -import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter; -import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; - -public interface ManagedBDBHAMessageStore -{ - public static final String TYPE = "BDBHAMessageStore"; - - public static final String ATTR_GROUP_NAME = "GroupName"; - public static final String ATTR_NODE_NAME = "NodeName"; - public static final String ATTR_NODE_HOST_PORT = "NodeHostPort"; - public static final String ATTR_HELPER_HOST_PORT = "HelperHostPort"; - public static final String ATTR_DURABILITY = "Durability"; - public static final String ATTR_NODE_STATE = "NodeState"; - public static final String ATTR_DESIGNATED_PRIMARY = "DesignatedPrimary"; - public static final String ATTR_COALESCING_SYNC = "CoalescingSync"; - - @MBeanAttribute(name=ATTR_GROUP_NAME, description="Name identifying the group") - String getGroupName() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_NODE_NAME, description="Unique name identifying the node within the group") - String getNodeName() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_NODE_HOST_PORT, description="Host/port used to replicate data between this node and others in the group") - String getNodeHostPort() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_NODE_STATE, description="Current state of this node") - String getNodeState() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_HELPER_HOST_PORT, description="Host/port used to allow a new node to discover other group members") - String getHelperHostPort() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_DURABILITY, description="Durability") - String getDurability() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_DESIGNATED_PRIMARY, description="Designated primary flag. Applicable to the two node case.") - boolean getDesignatedPrimary() throws IOException, JMException; - - @MBeanAttribute(name=ATTR_COALESCING_SYNC, description="Coalescing sync flag. Applicable to the master sync policies NO_SYNC and WRITE_NO_SYNC only.") - boolean getCoalescingSync() throws IOException, JMException; - - @MBeanAttribute(name="getAllNodesInGroup", description="Get all nodes within the group, regardless of whether currently attached or not") - TabularData getAllNodesInGroup() throws IOException, JMException; - - @MBeanOperation(name="removeNodeFromGroup", description="Remove an existing node from the group") - void removeNodeFromGroup(@MBeanOperationParameter(name="nodeName", description="name of node")String nodeName) throws JMException; - - @MBeanOperation(name="setDesignatedPrimary", description="Set/unset this node as the designated primary for the group. Applicable to the two node case.") - void setDesignatedPrimary(@MBeanOperationParameter(name="primary", description="designated primary")boolean primary) throws JMException; - - @MBeanOperation(name="updateAddress", description="Update the address of another node. The node must be in a STOPPED state.") - void updateAddress(@MBeanOperationParameter(name="nodeName", description="name of node")String nodeName, - @MBeanOperationParameter(name="newHostName", description="new hostname")String newHostName, - @MBeanOperationParameter(name="newPort", description="new port number")int newPort) throws JMException; -} - diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java index 3265fb6823..97a3d61df1 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java @@ -45,6 +45,7 @@ import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.store.berkeleydb.AMQShortStringEncoding; import org.apache.qpid.server.store.berkeleydb.FieldTableEncoding; import org.apache.qpid.server.util.MapJsonSerializer; @@ -93,6 +94,8 @@ public class UpgradeFrom5To6 extends AbstractStoreUpgrade private MapJsonSerializer _serializer = new MapJsonSerializer(); + private static final boolean _moveNonExclusiveQueueOwnerToDescription = Boolean.parseBoolean(System.getProperty("qpid.move_non_exclusive_queue_owner_to_description", Boolean.TRUE.toString())); + /** * Upgrades from a v5 database to a v6 database * @@ -554,17 +557,49 @@ public class UpgradeFrom5To6 extends AbstractStoreUpgrade private UpgradeConfiguredObjectRecord createQueueConfiguredObjectRecord(String queueName, String owner, boolean exclusive, FieldTable arguments) { + Map attributesMap = buildQueueArgumentMap(queueName, + owner, exclusive, arguments); + String json = _serializer.serialize(attributesMap); + UpgradeConfiguredObjectRecord configuredObject = new UpgradeConfiguredObjectRecord(Queue.class.getName(), json); + return configuredObject; + } + + private Map buildQueueArgumentMap(String queueName, + String owner, boolean exclusive, FieldTable arguments) + { + Map attributesMap = new HashMap(); attributesMap.put(Queue.NAME, queueName); - attributesMap.put(Queue.OWNER, owner); attributesMap.put(Queue.EXCLUSIVE, exclusive); + + FieldTable argumentsCopy = new FieldTable(); if (arguments != null) { - attributesMap.put("ARGUMENTS", FieldTable.convertToMap(arguments)); + argumentsCopy.addAll(arguments); } - String json = _serializer.serialize(attributesMap); - UpgradeConfiguredObjectRecord configuredObject = new UpgradeConfiguredObjectRecord(Queue.class.getName(), json); - return configuredObject; + + if (moveNonExclusiveOwnerToDescription(owner, exclusive)) + { + _logger.info("Non-exclusive owner " + owner + " for queue " + queueName + " moved to " + AMQQueueFactory.X_QPID_DESCRIPTION); + + attributesMap.put(Queue.OWNER, null); + argumentsCopy.put(AMQShortString.valueOf(AMQQueueFactory.X_QPID_DESCRIPTION), owner); + } + else + { + attributesMap.put(Queue.OWNER, owner); + } + if (!argumentsCopy.isEmpty()) + { + attributesMap.put(Queue.ARGUMENTS, FieldTable.convertToMap(argumentsCopy)); + } + return attributesMap; + } + + private boolean moveNonExclusiveOwnerToDescription(String owner, + boolean exclusive) + { + return exclusive == false && owner != null && _moveNonExclusiveQueueOwnerToDescription; } private UpgradeConfiguredObjectRecord createExchangeConfiguredObjectRecord(String exchangeName, String exchangeType, diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBeanTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBeanTest.java deleted file mode 100644 index b64a213756..0000000000 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBHAMessageStoreManagerMBeanTest.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store.berkeleydb; - -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.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeType; -import javax.management.openmbean.TabularData; - -import junit.framework.TestCase; - -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.logging.SystemOutMessageLogger; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.TestLogActor; - -public class BDBHAMessageStoreManagerMBeanTest extends TestCase -{ - private static final String TEST_GROUP_NAME = "testGroupName"; - private static final String TEST_NODE_NAME = "testNodeName"; - private static final String TEST_NODE_HOST_PORT = "host:1234"; - private static final String TEST_HELPER_HOST_PORT = "host:5678"; - private static final String TEST_DURABILITY = "sync,sync,all"; - private static final String TEST_NODE_STATE = "MASTER"; - private static final String TEST_STORE_NAME = "testStoreName"; - private static final boolean TEST_DESIGNATED_PRIMARY_FLAG = false; - - private BDBHAMessageStore _store; - private BDBHAMessageStoreManagerMBean _mBean; - - @Override - protected void setUp() throws Exception - { - super.setUp(); - - CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); - _store = mock(BDBHAMessageStore.class); - _mBean = new BDBHAMessageStoreManagerMBean(_store); - } - - @Override - protected void tearDown() throws Exception - { - super.tearDown(); - CurrentActor.remove(); - } - - public void testObjectName() throws Exception - { - when(_store.getName()).thenReturn(TEST_STORE_NAME); - - String expectedObjectName = "org.apache.qpid:type=BDBHAMessageStore,name=" + TEST_STORE_NAME; - assertEquals(expectedObjectName, _mBean.getObjectName().toString()); - } - - public void testGroupName() throws Exception - { - when(_store.getGroupName()).thenReturn(TEST_GROUP_NAME); - - assertEquals(TEST_GROUP_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_GROUP_NAME)); - } - - public void testNodeName() throws Exception - { - when(_store.getNodeName()).thenReturn(TEST_NODE_NAME); - - assertEquals(TEST_NODE_NAME, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_NAME)); - } - - public void testNodeHostPort() throws Exception - { - when(_store.getNodeHostPort()).thenReturn(TEST_NODE_HOST_PORT); - - assertEquals(TEST_NODE_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_HOST_PORT)); - } - - public void testHelperHostPort() throws Exception - { - when(_store.getHelperHostPort()).thenReturn(TEST_HELPER_HOST_PORT); - - assertEquals(TEST_HELPER_HOST_PORT, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_HELPER_HOST_PORT)); - } - - public void testDurability() throws Exception - { - when(_store.getDurability()).thenReturn(TEST_DURABILITY); - - assertEquals(TEST_DURABILITY, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DURABILITY)); - } - - public void testCoalescingSync() throws Exception - { - when(_store.isCoalescingSync()).thenReturn(true); - - assertEquals(true, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_COALESCING_SYNC)); - } - - public void testNodeState() throws Exception - { - when(_store.getNodeState()).thenReturn(TEST_NODE_STATE); - - assertEquals(TEST_NODE_STATE, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_NODE_STATE)); - } - - public void testDesignatedPrimaryFlag() throws Exception - { - when(_store.isDesignatedPrimary()).thenReturn(TEST_DESIGNATED_PRIMARY_FLAG); - - assertEquals(TEST_DESIGNATED_PRIMARY_FLAG, _mBean.getAttribute(ManagedBDBHAMessageStore.ATTR_DESIGNATED_PRIMARY)); - } - - public void testGroupMembersForGroupWithOneNode() throws Exception - { - List> members = Collections.singletonList(createTestNodeResult()); - when(_store.getGroupMembers()).thenReturn(members); - - final TabularData resultsTable = _mBean.getAllNodesInGroup(); - - assertTableHasHeadingsNamed(resultsTable, BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT); - - final int numberOfDataRows = resultsTable.size(); - assertEquals("Unexpected number of data rows", 1 ,numberOfDataRows); - final CompositeData row = (CompositeData) resultsTable.values().iterator().next(); - assertEquals(TEST_NODE_NAME, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME)); - assertEquals(TEST_NODE_HOST_PORT, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT)); - } - - public void testRemoveNodeFromReplicationGroup() throws Exception - { - _mBean.removeNodeFromGroup(TEST_NODE_NAME); - - verify(_store).removeNodeFromGroup(TEST_NODE_NAME); - } - - public void testRemoveNodeFromReplicationGroupWithError() throws Exception - { - doThrow(new AMQStoreException("mocked exception")).when(_store).removeNodeFromGroup(TEST_NODE_NAME); - - try - { - _mBean.removeNodeFromGroup(TEST_NODE_NAME); - fail("Exception not thrown"); - } - catch (JMException je) - { - // PASS - } - } - - public void testSetAsDesignatedPrimary() throws Exception - { - _mBean.setDesignatedPrimary(true); - - verify(_store).setDesignatedPrimary(true); - } - - public void testSetAsDesignatedPrimaryWithError() throws Exception - { - doThrow(new AMQStoreException("mocked exception")).when(_store).setDesignatedPrimary(true); - - try - { - _mBean.setDesignatedPrimary(true); - fail("Exception not thrown"); - } - catch (JMException je) - { - // PASS - } - } - - public void testUpdateAddress() throws Exception - { - String newHostName = "newHostName"; - int newPort = 1967; - - _mBean.updateAddress(TEST_NODE_NAME, newHostName, newPort); - - verify(_store).updateAddress(TEST_NODE_NAME, newHostName, newPort); - } - - private void assertTableHasHeadingsNamed(final TabularData resultsTable, String... headingNames) - { - CompositeType headingsRow = resultsTable.getTabularType().getRowType(); - for (final String headingName : headingNames) - { - assertTrue("Table should have column with heading " + headingName, headingsRow.containsKey(headingName)); - } - } - - private Map createTestNodeResult() - { - Map items = new HashMap(); - items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_NAME, TEST_NODE_NAME); - items.put(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT, TEST_NODE_HOST_PORT); - return items; - } -} diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreConfigurationTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreConfigurationTest.java index 687c671566..5cc436a22a 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreConfigurationTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreConfigurationTest.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.server.store.berkeleydb; import org.apache.qpid.server.store.DurableConfigurationStoreTest; diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java index 591bc27d1e..e97323c5f2 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java @@ -55,7 +55,7 @@ import org.apache.qpid.transport.MessageTransfer; /** * Subclass of MessageStoreTest which runs the standard tests from the superclass against - * the BDB Store as well as additional tests specific to the DBB store-implementation. + * the BDB Store as well as additional tests specific to the BDB store-implementation. */ public class BDBMessageStoreTest extends org.apache.qpid.server.store.MessageStoreTest { diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java deleted file mode 100644 index b01f169715..0000000000 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterManagementTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store.berkeleydb; - -import static com.sleepycat.je.rep.ReplicatedEnvironment.State.DETACHED; -import static com.sleepycat.je.rep.ReplicatedEnvironment.State.MASTER; -import static com.sleepycat.je.rep.ReplicatedEnvironment.State.REPLICA; -import static com.sleepycat.je.rep.ReplicatedEnvironment.State.UNKNOWN; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import javax.jms.Connection; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularData; - -import org.apache.log4j.Logger; -import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.test.utils.JMXTestUtils; -import org.apache.qpid.test.utils.QpidBrokerTestCase; - -import com.sleepycat.je.EnvironmentFailureException; - -/** - * System test verifying the ability to control a cluster via the Management API. - * - * @see HAClusterBlackboxTest - */ -public class HAClusterManagementTest extends QpidBrokerTestCase -{ - protected static final Logger LOGGER = Logger.getLogger(HAClusterManagementTest.class); - - private static final Set NON_MASTER_STATES = new HashSet(Arrays.asList(REPLICA.toString(), DETACHED.toString(), UNKNOWN.toString()));; - private static final String VIRTUAL_HOST = "test"; - - private static final String MANAGED_OBJECT_QUERY = "org.apache.qpid:type=BDBHAMessageStore,name=" + VIRTUAL_HOST; - private static final int NUMBER_OF_NODES = 4; - - private final HATestClusterCreator _clusterCreator = new HATestClusterCreator(this, VIRTUAL_HOST, NUMBER_OF_NODES); - private final JMXTestUtils _jmxUtils = new JMXTestUtils(this); - - private ConnectionURL _brokerFailoverUrl; - - @Override - protected void setUp() throws Exception - { - _brokerType = BrokerType.SPAWNED; - _jmxUtils.setUp(); - - _clusterCreator.configureClusterNodes(); - _brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes(); - _clusterCreator.startCluster(); - - super.setUp(); - } - - @Override - protected void tearDown() throws Exception - { - try - { - _jmxUtils.close(); - } - finally - { - super.tearDown(); - } - } - - @Override - public void startBroker() throws Exception - { - // Don't start default broker provided by QBTC. - } - - public void testReadonlyMBeanAttributes() throws Exception - { - final int brokerPortNumber = getBrokerPortNumbers().iterator().next(); - final int bdbPortNumber = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumber); - - ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumber); - assertEquals("Unexpected store group name", _clusterCreator.getGroupName(), storeBean.getGroupName()); - assertEquals("Unexpected store node name", _clusterCreator.getNodeNameForNodeAt(bdbPortNumber), storeBean.getNodeName()); - assertEquals("Unexpected store node host port",_clusterCreator.getNodeHostPortForNodeAt(bdbPortNumber), storeBean.getNodeHostPort()); - assertEquals("Unexpected store helper host port", _clusterCreator.getHelperHostPort(), storeBean.getHelperHostPort()); - // As we have chosen an arbitrary broker from the cluster, we cannot predict its state - assertNotNull("Store state must not be null", storeBean.getNodeState()); - } - - public void testStateOfActiveBrokerIsMaster() throws Exception - { - final Connection activeConnection = getConnection(_brokerFailoverUrl); - final int activeBrokerPortNumber = _clusterCreator.getBrokerPortNumberFromConnection(activeConnection); - - ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(activeBrokerPortNumber); - assertEquals("Unexpected store state", MASTER.toString(), storeBean.getNodeState()); - } - - public void testStateOfNonActiveBrokerIsNotMaster() throws Exception - { - final Connection activeConnection = getConnection(_brokerFailoverUrl); - final int inactiveBrokerPortNumber = _clusterCreator.getPortNumberOfAnInactiveBroker(activeConnection); - ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(inactiveBrokerPortNumber); - final String nodeState = storeBean.getNodeState(); - assertTrue("Unexpected store state : " + nodeState, NON_MASTER_STATES.contains(nodeState)); - } - - public void testGroupMembers() throws Exception - { - final int brokerPortNumber = getBrokerPortNumbers().iterator().next(); - - ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumber); - final TabularData groupMembers = storeBean.getAllNodesInGroup(); - assertNotNull(groupMembers); - - final int numberOfDataRows = groupMembers.size(); - assertEquals("Unexpected number of data rows", NUMBER_OF_NODES ,numberOfDataRows); - - for(int bdbPortNumber : _clusterCreator.getBdbPortNumbers()) - { - final String nodeName = _clusterCreator.getNodeNameForNodeAt(bdbPortNumber); - final String nodeHostPort = _clusterCreator.getNodeHostPortForNodeAt(bdbPortNumber); - - CompositeData row = groupMembers.get(new Object[] {nodeName}); - assertNotNull("Table does not contain row for node name " + nodeName, row); - assertEquals(nodeHostPort, row.get(BDBHAMessageStore.GRP_MEM_COL_NODE_HOST_PORT)); - } - } - - public void testRemoveNodeFromGroup() throws Exception - { - final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); - final int brokerPortNumberToMakeObservation = brokerPortNumberIterator.next(); - final int brokerPortNumberToBeRemoved = brokerPortNumberIterator.next(); - final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToMakeObservation); - final int numberOfDataRows = storeBean.getAllNodesInGroup().size(); - assertEquals("Unexpected number of data rows before test", NUMBER_OF_NODES ,numberOfDataRows); - - final String removedNodeName = _clusterCreator.getNodeNameForNodeAt(_clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeRemoved)); - _clusterCreator.stopNode(brokerPortNumberToBeRemoved); - storeBean.removeNodeFromGroup(removedNodeName); - - final int numberOfDataRowsAfterRemoval = storeBean.getAllNodesInGroup().size(); - assertEquals("Unexpected number of data rows before test", NUMBER_OF_NODES - 1,numberOfDataRowsAfterRemoval); - } - - /** - * Updates the address of a node. - * - * If the broker (node) can subsequently start without error then the update was a success, hence no need for an explicit - * assert. - * - * @see #testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() for converse case - */ - public void testUpdateAddress() throws Exception - { - final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); - final int brokerPortNumberToPerformUpdate = brokerPortNumberIterator.next(); - final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next(); - final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(brokerPortNumberToPerformUpdate); - - _clusterCreator.stopNode(brokerPortNumberToBeMoved); - - final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved); - final int newBdbPort = getNextAvailable(oldBdbPort + 1); - - storeBean.updateAddress(_clusterCreator.getNodeNameForNodeAt(oldBdbPort), _clusterCreator.getIpAddressOfBrokerHost(), newBdbPort); - - _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort); - - _clusterCreator.startNode(brokerPortNumberToBeMoved); - } - - /** - * @see #testUpdateAddress() - */ - public void testRestartNodeWithNewPortNumberWithoutFirstCallingUpdateAddressThrowsAnException() throws Exception - { - final Iterator brokerPortNumberIterator = getBrokerPortNumbers().iterator(); - final int brokerPortNumberToBeMoved = brokerPortNumberIterator.next(); - - _clusterCreator.stopNode(brokerPortNumberToBeMoved); - - final int oldBdbPort = _clusterCreator.getBdbPortForBrokerPort(brokerPortNumberToBeMoved); - final int newBdbPort = getNextAvailable(oldBdbPort + 1); - - // now deliberately don't call updateAddress - - _clusterCreator.modifyClusterNodeBdbAddress(brokerPortNumberToBeMoved, newBdbPort); - - try - { - _clusterCreator.startNode(brokerPortNumberToBeMoved); - fail("Exception not thrown"); - } - catch(RuntimeException rte) - { - //check cause was BDBs EnvironmentFailureException - assertTrue(rte.getMessage().contains(EnvironmentFailureException.class.getName())); - // PASS - } - } - - private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort( - final int activeBrokerPortNumber) throws Exception - { - _jmxUtils.open(activeBrokerPortNumber); - - return _jmxUtils.getManagedObject(ManagedBDBHAMessageStore.class, MANAGED_OBJECT_QUERY); - } -} diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java deleted file mode 100644 index 294859832f..0000000000 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/HAClusterTwoNodeTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.store.berkeleydb; - -import java.io.File; - -import javax.jms.Connection; -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.Session; - -import org.apache.qpid.jms.ConnectionURL; -import org.apache.qpid.test.utils.JMXTestUtils; -import org.apache.qpid.test.utils.QpidBrokerTestCase; - -import com.sleepycat.je.rep.ReplicationConfig; - -public class HAClusterTwoNodeTest extends QpidBrokerTestCase -{ - private static final long RECEIVE_TIMEOUT = 5000l; - - private static final String VIRTUAL_HOST = "test"; - - private static final String MANAGED_OBJECT_QUERY = "org.apache.qpid:type=BDBHAMessageStore,name=" + VIRTUAL_HOST; - private static final int NUMBER_OF_NODES = 2; - - private final HATestClusterCreator _clusterCreator = new HATestClusterCreator(this, VIRTUAL_HOST, NUMBER_OF_NODES); - private final JMXTestUtils _jmxUtils = new JMXTestUtils(this); - - private ConnectionURL _brokerFailoverUrl; - - @Override - protected void setUp() throws Exception - { - _brokerType = BrokerType.SPAWNED; - - assertTrue(isJavaBroker()); - assertTrue(isBrokerStorePersistent()); - _jmxUtils.setUp(); - - super.setUp(); - } - - @Override - protected void tearDown() throws Exception - { - try - { - _jmxUtils.close(); - } - finally - { - super.tearDown(); - } - } - - @Override - public void startBroker() throws Exception - { - // Don't start default broker provided by QBTC. - } - - private void startCluster(boolean designedPrimary) throws Exception - { - setSystemProperty("java.util.logging.config.file", "etc" + File.separator + "log.properties"); - - String storeConfigKeyPrefix = _clusterCreator.getStoreConfigKeyPrefix(); - - setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).name", ReplicationConfig.INSUFFICIENT_REPLICAS_TIMEOUT); - setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(0).value", "2 s"); - - setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).name", ReplicationConfig.ELECTIONS_PRIMARY_RETRIES); - setConfigurationProperty(storeConfigKeyPrefix + ".repConfig(1).value", "0"); - - _clusterCreator.configureClusterNodes(); - _clusterCreator.setDesignatedPrimaryOnFirstBroker(designedPrimary); - _brokerFailoverUrl = _clusterCreator.getConnectionUrlForAllClusterNodes(); - _clusterCreator.startCluster(); - } - - public void testMasterDesignatedPrimaryCanBeRestartedWithoutReplica() throws Exception - { - startCluster(true); - final Connection initialConnection = getConnection(_brokerFailoverUrl); - int masterPort = _clusterCreator.getBrokerPortNumberFromConnection(initialConnection); - assertProducingConsuming(initialConnection); - initialConnection.close(); - _clusterCreator.stopCluster(); - _clusterCreator.startNode(masterPort); - final Connection secondConnection = getConnection(_brokerFailoverUrl); - assertProducingConsuming(secondConnection); - secondConnection.close(); - } - - public void testClusterRestartWithoutDesignatedPrimary() throws Exception - { - startCluster(false); - final Connection initialConnection = getConnection(_brokerFailoverUrl); - assertProducingConsuming(initialConnection); - initialConnection.close(); - _clusterCreator.stopCluster(); - _clusterCreator.startClusterParallel(); - final Connection secondConnection = getConnection(_brokerFailoverUrl); - assertProducingConsuming(secondConnection); - secondConnection.close(); - } - - public void testDesignatedPrimaryContinuesAfterSecondaryStopped() throws Exception - { - startCluster(true); - _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); - final Connection connection = getConnection(_brokerFailoverUrl); - assertNotNull("Expected to get a valid connection to primary", connection); - assertProducingConsuming(connection); - } - - public void testPersistentOperationsFailOnNonDesignatedPrimarysAfterSecondaryStopped() throws Exception - { - startCluster(false); - _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); - final Connection connection = getConnection(_brokerFailoverUrl); - assertNotNull("Expected to get a valid connection to primary", connection); - try - { - assertProducingConsuming(connection); - fail("JMS peristent operations succeded on Master 'not designated primary' buy they should fail as replica is not available"); - } - catch(JMSException e) - { - // JMSException should be thrown on transaction start/commit - } - } - - public void testSecondaryDoesNotBecomePrimaryWhenDesignatedPrimaryStopped() throws Exception - { - startCluster(true); - _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfPrimary()); - - try - { - getConnection(_brokerFailoverUrl); - fail("Connection not expected"); - } - catch (JMSException e) - { - // PASS - } - } - - public void testInitialDesignatedPrimaryStateOfNodes() throws Exception - { - startCluster(true); - final ManagedBDBHAMessageStore primaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfPrimary()); - assertTrue("Expected primary node to be set as designated primary", primaryStoreBean.getDesignatedPrimary()); - - final ManagedBDBHAMessageStore secondaryStoreBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); - assertFalse("Expected secondary node to NOT be set as designated primary", secondaryStoreBean.getDesignatedPrimary()); - } - - public void testSecondaryDesignatedAsPrimaryAfterOrginalPrimaryStopped() throws Exception - { - startCluster(true); - _clusterCreator.stopNode(_clusterCreator.getBrokerPortNumberOfPrimary()); - final ManagedBDBHAMessageStore storeBean = getStoreBeanForNodeAtBrokerPort(_clusterCreator.getBrokerPortNumberOfSecondaryNode()); - - assertFalse("Expected node to NOT be set as designated primary", storeBean.getDesignatedPrimary()); - storeBean.setDesignatedPrimary(true); - assertTrue("Expected node to now be set as designated primary", storeBean.getDesignatedPrimary()); - - final Connection connection = getConnection(_brokerFailoverUrl); - assertNotNull("Expected to get a valid connection to new primary", connection); - assertProducingConsuming(connection); - } - - private ManagedBDBHAMessageStore getStoreBeanForNodeAtBrokerPort( - final int activeBrokerPortNumber) throws Exception - { - _jmxUtils.open(activeBrokerPortNumber); - - ManagedBDBHAMessageStore storeBean = _jmxUtils.getManagedObject(ManagedBDBHAMessageStore.class, MANAGED_OBJECT_QUERY); - return storeBean; - } - - private void assertProducingConsuming(final Connection connection) throws JMSException, Exception - { - Session session = connection.createSession(true, Session.SESSION_TRANSACTED); - Destination destination = session.createQueue(getTestQueueName()); - MessageConsumer consumer = session.createConsumer(destination); - sendMessage(session, destination, 1); - connection.start(); - Message m1 = consumer.receive(RECEIVE_TIMEOUT); - assertNotNull("Message 1 is not received", m1); - assertEquals("Unexpected first message received", 0, m1.getIntProperty(INDEX)); - session.commit(); - } - -} diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java index 36991b90d0..cd2654f79f 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java @@ -52,8 +52,8 @@ public abstract class AbstractUpgradeTestCase extends QpidTestCase } public static final String[] QUEUE_NAMES = { "clientid:myDurSubName", "clientid:mySelectorDurSubName", "myUpgradeQueue", - "queue-non-durable" }; - public static int[] QUEUE_SIZES = { 1, 1, 10, 3 }; + "queue-non-durable", "nonexclusive-with-erroneous-owner" }; + public static int[] QUEUE_SIZES = { 1, 1, 10, 3, 0}; public static int TOTAL_MESSAGE_NUMBER = 15; protected static final LogSubject LOG_SUBJECT = new TestBlankSubject(); diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java index 3f9e4e4aa1..65a8bb03fb 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java @@ -23,10 +23,13 @@ package org.apache.qpid.server.store.berkeleydb.upgrade; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQShortString; @@ -49,6 +52,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase { private static final String NON_DURABLE_QUEUE = BDBStoreUpgradeTestPreparer.NON_DURABLE_QUEUE_NAME; private static final String DURABLE_QUEUE = BDBStoreUpgradeTestPreparer.QUEUE_NAME; + private static final String NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER = "nonexclusive-with-erroneous-owner"; private static final String DURABLE_SUBSCRIPTION_QUEUE_WITH_SELECTOR = "clientid:mySelectorDurSubName"; private static final String DURABLE_SUBSCRIPTION_QUEUE = "clientid:myDurSubName"; private static final String EXCHANGE_DB_NAME = "exchangeDb_v5"; @@ -87,6 +91,10 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase BDBStoreUpgradeTestPreparer.SELECTOR_TOPIC_NAME, "testprop='true'"); assertBindingRecord(queueBindings, DURABLE_QUEUE, "amq.direct", DURABLE_QUEUE, null); assertBindingRecord(queueBindings, NON_DURABLE_QUEUE, "amq.direct", NON_DURABLE_QUEUE, null); + assertBindingRecord(queueBindings, NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER, "amq.direct", NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER, null); + + assertQueueHasOwner(NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER, "misused-owner-as-description"); + assertContent(); } @@ -94,7 +102,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase { UpgradeFrom4To5 upgrade = new UpgradeFrom4To5(); upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.NO), getVirtualHostName()); - assertQueues(new HashSet(Arrays.asList(DURABLE_SUBSCRIPTION_QUEUE, DURABLE_SUBSCRIPTION_QUEUE_WITH_SELECTOR, DURABLE_QUEUE))); + assertQueues(new HashSet(Arrays.asList(DURABLE_SUBSCRIPTION_QUEUE, DURABLE_SUBSCRIPTION_QUEUE_WITH_SELECTOR, DURABLE_QUEUE, NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER))); assertDatabaseRecordCount(DELIVERY_DB_NAME, 12); assertDatabaseRecordCount(MESSAGE_META_DATA_DB_NAME, 12); @@ -112,6 +120,9 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase assertBindingRecord(queueBindings, DURABLE_SUBSCRIPTION_QUEUE_WITH_SELECTOR, "amq.topic", BDBStoreUpgradeTestPreparer.SELECTOR_TOPIC_NAME, "testprop='true'"); assertBindingRecord(queueBindings, DURABLE_QUEUE, "amq.direct", DURABLE_QUEUE, null); + + assertQueueHasOwner(NON_EXCLUSIVE_WITH_ERRONEOUS_OWNER, "misused-owner-as-description"); + assertContent(); } @@ -257,7 +268,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase private void assertQueues(Set expectedQueueNames) { - List durableSubNames = new ArrayList(); + List durableSubNames = Collections.emptyList(); final UpgradeFrom4To5.QueueRecordBinding binding = new UpgradeFrom4To5.QueueRecordBinding(durableSubNames); final Set actualQueueNames = new HashSet(); @@ -278,6 +289,35 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase assertEquals("Unexpected queue names", expectedQueueNames, actualQueueNames); } + private void assertQueueHasOwner(String queueName, final String expectedOwner) + { + List durableSubNames = Collections.emptyList(); + final UpgradeFrom4To5.QueueRecordBinding binding = new UpgradeFrom4To5.QueueRecordBinding(durableSubNames); + final AtomicReference actualOwner = new AtomicReference(); + final AtomicBoolean foundQueue = new AtomicBoolean(false); + + CursorOperation queueNameCollector = new CursorOperation() + { + + @Override + public void processEntry(Database sourceDatabase, Database targetDatabase, Transaction transaction, + DatabaseEntry key, DatabaseEntry value) + { + QueueRecord record = binding.entryToObject(value); + String queueName = record.getNameShortString().asString(); + if (queueName.equals(queueName)) + { + foundQueue.set(true); + actualOwner.set(AMQShortString.toString(record.getOwner())); + } + } + }; + new DatabaseTemplate(_environment, "queueDb_v5", null).run(queueNameCollector); + + assertTrue("Could not find queue in database", foundQueue.get()); + assertEquals("Queue has unexpected owner", expectedOwner, actualOwner.get()); + } + private void assertContent() { final UpgradeFrom4To5.ContentBinding contentBinding = new UpgradeFrom4To5.ContentBinding(); diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java index 5297692820..0031447140 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java @@ -29,6 +29,7 @@ import static org.apache.qpid.server.store.berkeleydb.upgrade.UpgradeFrom5To6.OL import static org.apache.qpid.server.store.berkeleydb.upgrade.UpgradeFrom5To6.OLD_XID_DB_NAME; import java.io.File; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -41,6 +42,7 @@ import org.apache.qpid.server.model.Binding; import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.store.berkeleydb.entry.Xid; import org.apache.qpid.server.store.berkeleydb.tuple.XidBinding; import org.apache.qpid.server.store.berkeleydb.upgrade.UpgradeFrom5To6.CompoundKey; @@ -260,7 +262,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase private void assertDatabaseRecordCounts() { - assertDatabaseRecordCount(CONFIGURED_OBJECTS_DB_NAME, 9); + assertDatabaseRecordCount(CONFIGURED_OBJECTS_DB_NAME, 12); assertDatabaseRecordCount(NEW_DELIVERY_DB_NAME, 12); assertDatabaseRecordCount(NEW_METADATA_DB_NAME, 12); @@ -270,64 +272,25 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase private void assertConfiguredObjects() { Map configuredObjects = loadConfiguredObjects(); - assertEquals("Unexpected number of configured objects", 9, configuredObjects.size()); - - Set> expected = new HashSet>(9); - Map queue1 = new HashMap(); - queue1.put("exclusive", Boolean.FALSE); - queue1.put("name", "myUpgradeQueue"); - queue1.put("owner", null); - expected.add(queue1); - Map queue2 = new HashMap(); - queue2.put("exclusive", Boolean.TRUE); - queue2.put("name", "clientid:mySelectorDurSubName"); - queue2.put("owner", "clientid"); - expected.add(queue2); - Map queue3 = new HashMap(); - queue3.put("exclusive", Boolean.TRUE); - queue3.put("name", "clientid:myDurSubName"); - queue3.put("owner", "clientid"); - expected.add(queue3); - - Map queueBinding1 = new HashMap(); - queueBinding1.put("queue", UUIDGenerator.generateUUID("myUpgradeQueue", getVirtualHostName()).toString()); - queueBinding1.put("name", "myUpgradeQueue"); - queueBinding1.put("exchange", UUIDGenerator.generateUUID("<>", getVirtualHostName()).toString()); - expected.add(queueBinding1); - Map queueBinding2 = new HashMap(); - queueBinding2.put("queue", UUIDGenerator.generateUUID("myUpgradeQueue", getVirtualHostName()).toString()); - queueBinding2.put("name", "myUpgradeQueue"); - queueBinding2.put("exchange", UUIDGenerator.generateUUID("amq.direct", getVirtualHostName()).toString()); - Map arguments2 = new HashMap(); - arguments2.put("x-filter-jms-selector", ""); - queueBinding2.put("arguments", arguments2); - expected.add(queueBinding2); - Map queueBinding3 = new HashMap(); - queueBinding3.put("queue", UUIDGenerator.generateUUID("clientid:myDurSubName", getVirtualHostName()).toString()); - queueBinding3.put("name", "myUpgradeTopic"); - queueBinding3.put("exchange", UUIDGenerator.generateUUID("amq.topic", getVirtualHostName()).toString()); - Map arguments3 = new HashMap(); - arguments3.put("x-filter-jms-selector", ""); - queueBinding3.put("arguments", arguments3); - expected.add(queueBinding3); - Map queueBinding4 = new HashMap(); - queueBinding4.put("queue", UUIDGenerator.generateUUID("clientid:mySelectorDurSubName", getVirtualHostName()).toString()); - queueBinding4.put("name", "mySelectorUpgradeTopic"); - queueBinding4.put("exchange", UUIDGenerator.generateUUID("amq.topic", getVirtualHostName()).toString()); - Map arguments4 = new HashMap(); - arguments4.put("x-filter-jms-selector", "testprop='true'"); - queueBinding4.put("arguments", arguments4); - expected.add(queueBinding4); - Map queueBinding5 = new HashMap(); - queueBinding5.put("queue", UUIDGenerator.generateUUID("clientid:myDurSubName", getVirtualHostName()).toString()); - queueBinding5.put("name", "clientid:myDurSubName"); - queueBinding5.put("exchange", UUIDGenerator.generateUUID("<>", getVirtualHostName()).toString()); - expected.add(queueBinding5); - Map queueBinding6 = new HashMap(); - queueBinding6.put("queue", UUIDGenerator.generateUUID("clientid:mySelectorDurSubName", getVirtualHostName()).toString()); - queueBinding6.put("name", "clientid:mySelectorDurSubName"); - queueBinding6.put("exchange", UUIDGenerator.generateUUID("<>", getVirtualHostName()).toString()); - expected.add(queueBinding6); + assertEquals("Unexpected number of configured objects", 12, configuredObjects.size()); + + Set> expected = new HashSet>(12); + expected.add(createExpectedQueueMap("myUpgradeQueue", Boolean.FALSE, null, null)); + expected.add(createExpectedQueueMap("clientid:mySelectorDurSubName", Boolean.TRUE, "clientid", null)); + expected.add(createExpectedQueueMap("clientid:myDurSubName", Boolean.TRUE, "clientid", null)); + expected.add(createExpectedQueueMap("nonexclusive-with-erroneous-owner", Boolean.FALSE, null, + Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, "misused-owner-as-description"))); + + expected.add(createExpectedQueueBindingMap("myUpgradeQueue","myUpgradeQueue", "<>", null)); + expected.add(createExpectedQueueBindingMap("myUpgradeQueue", "myUpgradeQueue", "amq.direct", null)); + expected.add(createExpectedQueueBindingMap("clientid:myDurSubName", "myUpgradeTopic", "amq.topic", + Collections.singletonMap("x-filter-jms-selector", ""))); + expected.add(createExpectedQueueBindingMap("clientid:mySelectorDurSubName", "mySelectorUpgradeTopic", "amq.topic", + Collections.singletonMap("x-filter-jms-selector", "testprop='true'"))); + expected.add(createExpectedQueueBindingMap("clientid:myDurSubName", "clientid:myDurSubName", "<>", null)); + expected.add(createExpectedQueueBindingMap("clientid:mySelectorDurSubName", "clientid:mySelectorDurSubName", "<>", null)); + expected.add(createExpectedQueueBindingMap("nonexclusive-with-erroneous-owner", "nonexclusive-with-erroneous-owner", "amq.direct", null)); + expected.add(createExpectedQueueBindingMap("nonexclusive-with-erroneous-owner","nonexclusive-with-erroneous-owner", "<>", null)); Set expectedTypes = new HashSet(); expectedTypes.add(Queue.class.getName()); @@ -337,11 +300,11 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase for (Entry entry : configuredObjects.entrySet()) { UpgradeConfiguredObjectRecord object = entry.getValue(); - UUID key = entry.getKey(); Map deserialized = jsonSerializer.deserialize(object.getAttributes()); assertTrue("Unexpected entry:" + object.getAttributes(), expected.remove(deserialized)); String type = object.getType(); assertTrue("Unexpected type:" + type, expectedTypes.contains(type)); + UUID key = entry.getKey(); if (type.equals(Exchange.class.getName()) || type.equals(Queue.class.getName())) { assertEquals("Unexpected key", key, UUIDGenerator.generateUUID(((String) deserialized.get("name")), getVirtualHostName())); @@ -354,6 +317,32 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase assertTrue("Not all expected configured objects found:" + expected, expected.isEmpty()); } + private Map createExpectedQueueBindingMap(String queue, String bindingName, String exchangeName, Map argumentMap) + { + Map expectedQueueBinding = new HashMap(); + expectedQueueBinding.put(Binding.QUEUE, UUIDGenerator.generateUUID(queue, getVirtualHostName()).toString()); + expectedQueueBinding.put(Binding.NAME, bindingName); + expectedQueueBinding.put(Binding.EXCHANGE, UUIDGenerator.generateUUID(exchangeName, getVirtualHostName()).toString()); + if (argumentMap != null) + { + expectedQueueBinding.put(Binding.ARGUMENTS, argumentMap); + } + return expectedQueueBinding; + } + + private Map createExpectedQueueMap(String name, boolean exclusiveFlag, String owner, Map argumentMap) + { + Map expectedQueueEntry = new HashMap(); + expectedQueueEntry.put(Queue.NAME, name); + expectedQueueEntry.put(Queue.EXCLUSIVE, exclusiveFlag); + expectedQueueEntry.put(Queue.OWNER, owner); + if (argumentMap != null) + { + expectedQueueEntry.put(Queue.ARGUMENTS, argumentMap); + } + return expectedQueueEntry; + } + private Map loadConfiguredObjects() { final Map configuredObjectsRecords = new HashMap(); diff --git a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v4/test-store/00000000.jdb b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v4/test-store/00000000.jdb index 167ab7f0ca..f5ed9aa5a2 100644 Binary files a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v4/test-store/00000000.jdb and b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v4/test-store/00000000.jdb differ diff --git a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000000.jdb b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000000.jdb index d44b21a83e..f5ed9aa5a2 100644 Binary files a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000000.jdb and b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000000.jdb differ diff --git a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000001.jdb b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000001.jdb index 9b85860c19..d5ae8c1096 100644 Binary files a/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000001.jdb and b/qpid/java/bdbstore/src/test/resources/upgrade/bdbstore-v5/test-store/00000001.jdb differ diff --git a/qpid/java/broker-plugins/access-control/MANIFEST.MF b/qpid/java/broker-plugins/access-control/MANIFEST.MF index 78072850e4..a8fb99995e 100644 --- a/qpid/java/broker-plugins/access-control/MANIFEST.MF +++ b/qpid/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/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF b/qpid/java/broker-plugins/experimental/shutdown/MANIFEST.MF deleted file mode 100644 index 0bd0a835e4..0000000000 --- a/qpid/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/qpid/java/broker-plugins/experimental/shutdown/build.xml b/qpid/java/broker-plugins/experimental/shutdown/build.xml deleted file mode 100644 index 96f97ee437..0000000000 --- a/qpid/java/broker-plugins/experimental/shutdown/build.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Activator.java deleted file mode 100644 index 2b7fa33784..0000000000 --- a/qpid/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/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java deleted file mode 100644 index cef4a42b64..0000000000 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/Shutdown.java +++ /dev/null @@ -1,130 +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 java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.management.NotCompliantMBeanException; - -import org.apache.log4j.Logger; -import org.apache.qpid.server.management.DefaultManagedObject; - -/** - * Implementation of the JMX broker shutdown plugin. - */ -public class Shutdown extends DefaultManagedObject implements ShutdownMBean -{ - - private static final Logger _logger = Logger.getLogger(Shutdown.class); - - private static final String FORMAT = "yyyy/MM/dd HH:mm:ss"; - private static final int THREAD_COUNT = 1; - private static final ScheduledExecutorService EXECUTOR = new ScheduledThreadPoolExecutor(THREAD_COUNT); - - private final Runnable _shutdown = new SystemExiter(); - - public Shutdown() throws NotCompliantMBeanException - { - super(ShutdownMBean.class, ShutdownMBean.TYPE); - } - - /** @see ShutdownMBean#shutdown() */ - public void shutdown() - { - _logger.info("Shutting down at user's request"); - shutdownBroker(0); - } - - /** @see ShutdownMBean#shutdown(long) */ - public void shutdown(final long delay) - { - if (delay < 0) - { - _logger.info("Shutting down at user's request"); - shutdownBroker(0); - } - else - { - _logger.info("Scheduled broker shutdown after " + delay + "ms"); - shutdownBroker(delay); - } - } - - /** @see ShutdownMBean#shutdownAt(String) */ - public void shutdownAt(final String when) - { - Date date; - DateFormat df = new SimpleDateFormat(FORMAT); - try - { - date = df.parse(when); - } - catch (ParseException e) - { - _logger.error("Invalid date \"" + when + "\": expecting " + FORMAT, e); - return; - } - _logger.info("Scheduled broker shutdown at " + when); - long now = System.currentTimeMillis(); - long time = date.getTime(); - if (time > now) - { - shutdownBroker(time - now); - } - else - { - shutdownBroker(0); - } - } - - /** - * Submits the {@link SystemExiter} job to shutdown the broker. - */ - private void shutdownBroker(long delay) - { - EXECUTOR.schedule(_shutdown, delay, TimeUnit.MILLISECONDS); - } - - /** - * Shutting down the system in another thread to avoid JMX exceptions being thrown. - */ - class SystemExiter implements Runnable - { - public void run() - { - System.exit(0); - } - } - - /** - * @see org.apache.qpid.server.management.ManagedObject#getObjectInstanceName() - */ - @Override - public String getObjectInstanceName() - { - return "Shutdown"; - } -} diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.java deleted file mode 100644 index 5c54fb3e21..0000000000 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/org/apache/qpid/shutdown/ShutdownMBean.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 javax.management.MBeanOperationInfo; - -import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; -import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter; - -/** - * Shutdown plugin JMX MBean interface. - * - * Shuts the Qpid broker down via JMX. - */ -public interface ShutdownMBean -{ - static final String TYPE = "Shutdown"; - - /** - * Broker will be shut down immediately. - */ - @MBeanOperation(name="shutdown", description="Shut down immediately", impact = MBeanOperationInfo.ACTION) - public void shutdown(); - - /** - * Broker will be shutdown after the specified delay - * - * @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); - - /** - * Broker will be shutdown at the specified date and time. - * - * @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); -} diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd deleted file mode 100755 index 60af4b89e8..0000000000 --- a/qpid/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/qpid/java/broker-plugins/firewall/MANIFEST.MF b/qpid/java/broker-plugins/firewall/MANIFEST.MF index 6ceea119da..a302921d03 100644 --- a/qpid/java/broker-plugins/firewall/MANIFEST.MF +++ b/qpid/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/qpid/java/broker-plugins/jmx/MANIFEST.MF b/qpid/java/broker-plugins/jmx/MANIFEST.MF new file mode 100644 index 0000000000..b13ff7f132 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/MANIFEST.MF @@ -0,0 +1,65 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Qpid Broker-Plugins JMX +Bundle-SymbolicName: broker-plugins-jmx +Bundle-Description: 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.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/qpid/java/broker-plugins/jmx/build.xml b/qpid/java/broker-plugins/jmx/build.xml new file mode 100644 index 0000000000..4deb0196e7 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/build.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/AMQManagedObject.java new file mode 100644 index 0000000000..5c39a0c26a --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/DefaultManagedObject.java new file mode 100644 index 0000000000..4446f96802 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXActivator.java new file mode 100644 index 0000000000..c588b40de7 --- /dev/null +++ b/qpid/java/broker-plugins/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 _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 registerServices(BundleContext ctx) + { + if (LOGGER.isInfoEnabled()) + { + LOGGER.info("Registering jmx plugin: " + _bundleName); + } + + List serviceRegistrations = new ArrayList(); + + 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 iterator = _registeredServices.iterator(); iterator.hasNext();) + { + ServiceRegistration service = iterator.next(); + service.unregister(); + } + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXConfiguration.java new file mode 100644 index 0000000000..dc9a712f90 --- /dev/null +++ b/qpid/java/broker-plugins/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 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java new file mode 100644 index 0000000000..0648235077 --- /dev/null +++ b/qpid/java/broker-plugins/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 env = new HashMap(); + 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 connectionIdUsernameMap = new ConcurrentHashMap(); + 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java new file mode 100644 index 0000000000..7519cea4db --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.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 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.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.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 Map _children = new HashMap(); + + 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); + } + + 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 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 + * only those classes which are visible to the classloader of this OSGI bundle. + */ + private Iterator getMBeanProviderIterator() + { + return ServiceLoader.load(MBeanProvider.class, BUNDLE_CLASSLOADER).iterator(); + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanIntrospector.java new file mode 100644 index 0000000000..79ddc8cfc0 --- /dev/null +++ b/qpid/java/broker-plugins/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 attributesList = new ArrayList(); + + /** + * 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 operationsList = new ArrayList(); + + 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 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 constructors = new ArrayList(); + + 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java new file mode 100644 index 0000000000..49f06d5121 --- /dev/null +++ b/qpid/java/broker-plugins/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 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 connectionIdUsernameMap = (Map) 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/MBeanProvider.java new file mode 100644 index 0000000000..83909dbe72 --- /dev/null +++ b/qpid/java/broker-plugins/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 child 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObject.java new file mode 100644 index 0000000000..40b778fd93 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/ManagedObjectRegistry.java new file mode 100644 index 0000000000..2ae0ac7052 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java new file mode 100644 index 0000000000..4115f9f363 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/AbstractStatisticsGatheringMBean.java @@ -0,0 +1,196 @@ +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; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

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

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +abstract class AbstractStatisticsGatheringMBean 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConfigurationManagementMBean.java new file mode 100644 index 0000000000..beffb4eaa9 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java new file mode 100644 index 0000000000..024ee39318 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBean.java @@ -0,0 +1,183 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 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 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) + { + // TODO - Implement setStatisticsEnabled + 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java new file mode 100644 index 0000000000..eb7e716af8 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ExchangeMBean.java @@ -0,0 +1,323 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.management.common.mbeans.ManagedQueue; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParameter; +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.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 +{ + + 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()]); + public static final String HEADERS_EXCHANGE_TYPE = "headers"; + + 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 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 headerMappings = binding.getArguments(); + + final List mappingList = new ArrayList(); + + if(headerMappings != null) + { + for(Map.Entry 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 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> bindingMap = new HashMap>(); + + for (Binding binding : bindings) + { + String key = "fanout".equals(_exchange.getExchangeType()) ? "*" : binding.getName(); + List queueList = bindingMap.get(key); + if(queueList == null) + { + queueList = new ArrayList(); + bindingMap.put(key, queueList); + } + queueList.add(binding.getParent(Queue.class).getName()); + + } + + for(Map.Entry> 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 arguments = new HashMap(); + + 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 \"=,=\" "); + } + + 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]); + } + } + } + + Queue queue = null; + VirtualHost vhost = _exchange.getParent(VirtualHost.class); + for(Queue aQueue : vhost.getQueues()) + { + if(aQueue.getName().equals(queueName)) + { + queue = aQueue; + break; + } + } + _exchange.createBinding(binding, queue, arguments, Collections.EMPTY_MAP); + } + + public void removeBinding(String queueName, String bindingKey) + throws IOException, JMException + { + Queue queue = null; + VirtualHost vhost = _exchange.getParent(VirtualHost.class); + for(Queue aQueue : vhost.getQueues()) + { + if(aQueue.getName().equals(queueName)) + { + queue = aQueue; + break; + } + } + + for(Binding binding : _exchange.getBindings()) + { + if(queue.equals(binding.getParent(Queue.class)) && bindingKey.equals(binding.getName())) + { + binding.delete(); + } + } + + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java new file mode 100644 index 0000000000..9ff45979ca --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java @@ -0,0 +1,832 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.apache.log4j.xml.Log4jEntityResolver; +import org.apache.log4j.xml.QpidLog4JConfigurator; +import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException; +import org.apache.log4j.xml.QpidLog4JConfigurator.QpidLog4JSaxErrorHandler; +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.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; + +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.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static org.apache.log4j.xml.QpidLog4JConfigurator.LOCK; + + +/** MBean class for BrokerLoggingManagerMBean. It implements all the management features exposed for managing logging. */ +@MBeanDescription("Logging Management Interface") +public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement +{ + + private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class); + private String _log4jConfigFileName; + private int _log4jLogWatchInterval; + private static final String INHERITED = "INHERITED"; + private static final String[] LEVELS = new String[]{Level.ALL.toString(), Level.TRACE.toString(), + Level.DEBUG.toString(), Level.INFO.toString(), + Level.WARN.toString(), Level.ERROR.toString(), + Level.FATAL.toString(),Level.OFF.toString(), + INHERITED}; + private static TabularType _loggerLevelTabularType; + private static CompositeType _loggerLevelCompositeType; + + static + { + try + { + OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING}; + + _loggerLevelCompositeType = 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); + + _loggerLevelTabularType = new TabularType("LoggerLevel", "List of loggers with levels", + _loggerLevelCompositeType, + TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); + } + catch (OpenDataException e) + { + _logger.error("Tabular data setup for viewing logger levels was incorrect."); + _loggerLevelTabularType = null; + } + } + + public LoggingManagementMBean(String log4jConfigFileName, + int log4jLogWatchInterval, + ManagedObjectRegistry registry) throws JMException + { + super(LoggingManagement.class, LoggingManagement.TYPE, registry); + _log4jConfigFileName = log4jConfigFileName; + _log4jLogWatchInterval = log4jLogWatchInterval; + register(); + } + + public String getObjectInstanceName() + { + return LoggingManagement.TYPE; + } + + public Integer getLog4jLogWatchInterval() + { + return _log4jLogWatchInterval; + } + + public String[] getAvailableLoggerLevels() + { + return LEVELS; + } + @SuppressWarnings("unchecked") + public synchronized boolean setRuntimeLoggerLevel(String logger, String level) + { + //check specified level is valid + Level newLevel; + try + { + newLevel = getLevel(level); + } + catch (Exception e) + { + return false; + } + + //check specified logger exists + Enumeration loggers = LogManager.getCurrentLoggers(); + Boolean loggerExists = false; + + while(loggers.hasMoreElements()) + { + Logger log = (Logger) loggers.nextElement(); + if (log.getName().equals(logger)) + { + loggerExists = true; + break; + } + } + + if(!loggerExists) + { + return false; + } + + //set the logger to the new level + _logger.info("Setting level to " + level + " for logger: " + logger); + + Logger log = Logger.getLogger(logger); + log.setLevel(newLevel); + + return true; + } + + @SuppressWarnings("unchecked") + public synchronized TabularData viewEffectiveRuntimeLoggerLevels() + { + if (_loggerLevelTabularType == null) + { + _logger.warn("TabluarData type not set up correctly"); + return null; + } + + _logger.info("Getting levels for currently active log4j loggers"); + + Enumeration loggers = LogManager.getCurrentLoggers(); + + TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType); + + Logger logger; + String loggerName; + String level; + + try + { + while(loggers.hasMoreElements()){ + logger = (Logger) loggers.nextElement(); + + loggerName = logger.getName(); + level = logger.getEffectiveLevel().toString(); + + Object[] itemData = {loggerName, level}; + CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, + COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); + loggerLevelList.put(loggerData); + } + } + catch (OpenDataException e) + { + _logger.warn("Unable to create logger level list due to :" + e); + return null; + } + + return loggerLevelList; + + } + + public synchronized String getRuntimeRootLoggerLevel() + { + Logger rootLogger = Logger.getRootLogger(); + + return rootLogger.getLevel().toString(); + } + + public synchronized boolean setRuntimeRootLoggerLevel(String level) + { + Level newLevel; + try + { + newLevel = getLevel(level); + } + catch (Exception e) + { + return false; + } + + if(newLevel == null) + { + //A null Level reference implies inheritance. Setting the runtime RootLogger + //to null is catastrophic (and prevented by Log4J at startup and runtime anyway). + return false; + } + + _logger.info("Setting RootLogger level to " + level); + + Logger log = Logger.getRootLogger(); + log.setLevel(newLevel); + + return true; + } + + //method to convert from a string to a log4j Level, throws exception if the given value is invalid + private Level getLevel(String level) throws Exception + { + if("null".equalsIgnoreCase(level) || INHERITED.equalsIgnoreCase(level)) + { + //the string "null" or "inherited" signals to inherit from a parent logger, + //using a null Level reference for the logger. + return null; + } + + Level newLevel = Level.toLevel(level); + + //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result. + if (newLevel.equals(Level.DEBUG) && !(level.equalsIgnoreCase("debug"))) + { + //received DEBUG but we did not ask for it, the Level request failed. + throw new Exception("Invalid level name"); + } + + return newLevel; + } + + //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content. + private static synchronized Document parseConfigFile(String fileName) throws IOException + { + try + { + LOCK.lock(); + + //check file was specified, exists, and is readable + if(fileName == null) + { + _logger.warn("Provided log4j XML configuration filename is null"); + throw new IOException("Provided log4j XML configuration filename is null"); + } + + File configFile = new File(fileName); + + if (!configFile.exists()) + { + _logger.warn("The log4j XML configuration file could not be found: " + fileName); + throw new IOException("The log4j XML configuration file could not be found"); + } + else if (!configFile.canRead()) + { + _logger.warn("The log4j XML configuration file is not readable: " + fileName); + throw new IOException("The log4j XML configuration file is not readable"); + } + + //parse it + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder; + Document doc; + + ErrorHandler errHandler = new QpidLog4JSaxErrorHandler(); + try + { + docFactory.setValidating(true); + docBuilder = docFactory.newDocumentBuilder(); + docBuilder.setErrorHandler(errHandler); + docBuilder.setEntityResolver(new Log4jEntityResolver()); + doc = docBuilder.parse(fileName); + } + catch (ParserConfigurationException e) + { + _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e); + //recommended that MBeans should use java.* and javax.* exceptions only + throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage()); + } + catch (SAXException e) + { + _logger.warn("The specified log4j XML file is invalid: " + e); + //recommended that MBeans should use standard java.* and javax.* exceptions only + throw new IOException("The specified log4j XML file is invalid: " + e.getMessage()); + } + catch (IOException e) + { + _logger.warn("Unable to parse the specified log4j XML file" + e); + throw new IOException("Unable to parse the specified log4j XML file: " + e.getMessage()); + } + + return doc; + } + finally + { + LOCK.unlock(); + } + } + + + private static synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException + { + try + { + LOCK.lock(); + + File log4jConfigFile = new File(log4jConfigFileName); + + if (!log4jConfigFile.canWrite()) + { + _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile); + throw new IOException("Specified log4j XML configuration file is not writable"); + } + + Transformer transformer = null; + try + { + transformer = TransformerFactory.newInstance().newTransformer(); + } + catch (Exception e) + { + _logger.warn("Could not create an XML transformer: " +e); + return false; + } + + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd"); + DOMSource source = new DOMSource(doc); + + File tmp; + Random r = new Random(); + do + { + tmp = new File(log4jConfigFile.getPath() + r.nextInt() + ".tmp"); + } + while(tmp.exists()); + + tmp.deleteOnExit(); + + try + { + StreamResult result = new StreamResult(tmp); + transformer.transform(source, result); + } + catch (TransformerException e) + { + _logger.warn("Could not transform the XML into new file: " +e); + throw new IOException("Could not transform the XML into new file: " +e); + } + + // Swap temp file in to replace existing configuration file. + File old = new File(log4jConfigFile.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + + if(!log4jConfigFile.renameTo(old)) + { + //unable to rename the existing file to the backup name + _logger.error("Could not backup the existing log4j XML file"); + throw new IOException("Could not backup the existing log4j XML file"); + } + + if(!tmp.renameTo(log4jConfigFile)) + { + //failed to rename the new file to the required filename + + if(!old.renameTo(log4jConfigFile)) + { + //unable to return the backup to required filename + _logger.error("Could not rename the new log4j configuration file into place, and unable to restore original file"); + throw new IOException("Could not rename the new log4j configuration file into place, and unable to restore original file"); + } + + _logger.error("Could not rename the new log4j configuration file into place"); + throw new IOException("Could not rename the new log4j configuration file into place"); + } + + return true; + } + finally + { + LOCK.unlock(); + } + } + + + /* The log4j XML configuration file DTD defines three possible element + * combinations for specifying optional logger+level settings. + * Must account for the following: + * + * OR + * OR + * + * + * Noting also that the level/priority child element is optional too, + * and not the only possible child element. + */ + + public static synchronized Map retrieveConfigFileLoggersLevels(String fileName) throws IOException + { + try + { + LOCK.lock(); + + Document doc = parseConfigFile(fileName); + + HashMap loggerLevelList = new HashMap(); + + //retrieve the 'category' element nodes + NodeList categoryElements = doc.getElementsByTagName("category"); + + String categoryName; + String priority = null; + + for (int i = 0; i < categoryElements.getLength(); i++) + { + Element categoryElement = (Element) categoryElements.item(i); + categoryName = categoryElement.getAttribute("name"); + + //retrieve the category's mandatory 'priority' or 'level' element's value. + //It may not be the only child node, so request by tag name. + NodeList priorityElements = categoryElement.getElementsByTagName("priority"); + NodeList levelElements = categoryElement.getElementsByTagName("level"); + + if (priorityElements.getLength() != 0) + { + Element priorityElement = (Element) priorityElements.item(0); + priority = priorityElement.getAttribute("value"); + } + else if (levelElements.getLength() != 0) + { + Element levelElement = (Element) levelElements.item(0); + priority = levelElement.getAttribute("value"); + } + else + { + //there is no exiting priority or level to view, move onto next category/logger + continue; + } + + loggerLevelList.put(categoryName, priority); + } + + //retrieve the 'logger' element nodes + NodeList loggerElements = doc.getElementsByTagName("logger"); + + String loggerName; + String level; + + for (int i = 0; i < loggerElements.getLength(); i++) + { + Element loggerElement = (Element) loggerElements.item(i); + loggerName = loggerElement.getAttribute("name"); + + //retrieve the logger's mandatory 'level' element's value + //It may not be the only child node, so request by tag name. + NodeList levelElements = loggerElement.getElementsByTagName("level"); + + Element levelElement = (Element) levelElements.item(0); + level = levelElement.getAttribute("value"); + + loggerLevelList.put(loggerName, level); + } + + return loggerLevelList; + } + finally + { + LOCK.unlock(); + } + } + + public synchronized TabularData viewConfigFileLoggerLevels() throws IOException + { + try + { + LOCK.lock(); + + if (_loggerLevelTabularType == null) + { + _logger.warn("TabluarData type not set up correctly"); + return null; + } + + _logger.info("Getting logger levels from log4j configuration file"); + + TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType); + + Map levels = retrieveConfigFileLoggersLevels(_log4jConfigFileName); + + for (Map.Entry entry : levels.entrySet()) + { + String loggerName = entry.getKey(); + String level = entry.getValue(); + + try + { + Object[] itemData = {loggerName, level.toUpperCase()}; + CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, + COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); + loggerLevelList.put(loggerData); + } + catch (OpenDataException e) + { + _logger.warn("Unable to create logger level list due to :" + e); + return null; + } + } + + return loggerLevelList; + } + finally + { + LOCK.unlock(); + } + } + + public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException + { + try + { + LOCK.lock(); + + //check that the specified level is a valid log4j Level + try + { + getLevel(level); + } + catch (Exception e) + { + //it isnt a valid level + return false; + } + + _logger.info("Setting level to " + level + " for logger '" + logger + + "' in log4j xml configuration file: " + _log4jConfigFileName); + + Document doc = parseConfigFile(_log4jConfigFileName); + + //retrieve the 'category' and 'logger' element nodes + NodeList categoryElements = doc.getElementsByTagName("category"); + NodeList loggerElements = doc.getElementsByTagName("logger"); + + //collect them into a single elements list + List logElements = new ArrayList(); + + for (int i = 0; i < categoryElements.getLength(); i++) + { + logElements.add((Element) categoryElements.item(i)); + } + for (int i = 0; i < loggerElements.getLength(); i++) + { + logElements.add((Element) loggerElements.item(i)); + } + + //try to locate the specified logger/category in the elements retrieved + Element logElement = null; + for (Element e : logElements) + { + if (e.getAttribute("name").equals(logger)) + { + logElement = e; + break; + } + } + + if (logElement == null) + { + //no loggers/categories with given name found, does not exist to update + _logger.warn("Specified logger does not exist in the configuration file: " +logger); + return false; + } + + //retrieve the optional 'priority' or 'level' sub-element value. + //It may not be the only child node, so request by tag name. + NodeList priorityElements = logElement.getElementsByTagName("priority"); + NodeList levelElements = logElement.getElementsByTagName("level"); + + Element levelElement = null; + if (priorityElements.getLength() != 0) + { + levelElement = (Element) priorityElements.item(0); + } + else if (levelElements.getLength() != 0) + { + levelElement = (Element) levelElements.item(0); + } + else + { + //there is no exiting priority or level element to update + return false; + } + + //update the element with the new level/priority + levelElement.setAttribute("value", level.toLowerCase()); + + //output the new file + return writeUpdatedConfigFile(_log4jConfigFileName, doc); + } + finally + { + LOCK.unlock(); + } + } + + + /* The log4j XML configuration file DTD defines 2 possible element + * combinations for specifying the optional root logger level settings + * Must account for the following: + * + * OR + * + * + * Noting also that the level/priority child element is optional too, + * and not the only possible child element. + */ + + public static synchronized String retrieveConfigFileRootLoggerLevel(String fileName) throws IOException + { + try + { + LOCK.lock(); + + Document doc = parseConfigFile(fileName); + + //retrieve the optional 'root' element node + NodeList rootElements = doc.getElementsByTagName("root"); + + if (rootElements.getLength() == 0) + { + //there is no root logger definition + return "N/A"; + } + + Element rootElement = (Element) rootElements.item(0); + + //retrieve the optional 'priority' or 'level' element value. + //It may not be the only child node, so request by tag name. + NodeList priorityElements = rootElement.getElementsByTagName("priority"); + NodeList levelElements = rootElement.getElementsByTagName("level"); + String priority = null; + + if (priorityElements.getLength() != 0) + { + Element priorityElement = (Element) priorityElements.item(0); + priority = priorityElement.getAttribute("value"); + } + else if(levelElements.getLength() != 0) + { + Element levelElement = (Element) levelElements.item(0); + priority = levelElement.getAttribute("value"); + } + + if(priority != null) + { + return priority; + } + else + { + return "N/A"; + } + } + finally + { + LOCK.unlock(); + } + } + + public synchronized String getConfigFileRootLoggerLevel() throws IOException + { + return retrieveConfigFileRootLoggerLevel(_log4jConfigFileName).toUpperCase(); + } + + public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException + { + try + { + LOCK.lock(); + + //check that the specified level is a valid log4j Level + try + { + Level newLevel = getLevel(level); + if(newLevel == null) + { + //A null Level reference implies inheritance. Setting the config file RootLogger + //to "null" or "inherited" just ensures it defaults to DEBUG at startup as Log4J + //prevents this catastrophic situation at startup and runtime anyway. + return false; + } + } + catch (Exception e) + { + //it isnt a valid level + return false; + } + + _logger.info("Setting level to " + level + " for the Root logger in " + + "log4j xml configuration file: " + _log4jConfigFileName); + + Document doc = parseConfigFile(_log4jConfigFileName); + + //retrieve the optional 'root' element node + NodeList rootElements = doc.getElementsByTagName("root"); + + if (rootElements.getLength() == 0) + { + return false; + } + + Element rootElement = (Element) rootElements.item(0); + + //retrieve the optional 'priority' or 'level' sub-element value. + //It may not be the only child node, so request by tag name. + NodeList priorityElements = rootElement.getElementsByTagName("priority"); + NodeList levelElements = rootElement.getElementsByTagName("level"); + + Element levelElement = null; + if (priorityElements.getLength() != 0) + { + levelElement = (Element) priorityElements.item(0); + } + else if (levelElements.getLength() != 0) + { + levelElement = (Element) levelElements.item(0); + } + else + { + //there is no exiting priority/level to update + return false; + } + + //update the element with the new level/priority + levelElement.setAttribute("value", level); + + //output the new file + return writeUpdatedConfigFile(_log4jConfigFileName, doc); + } + finally + { + LOCK.unlock(); + } + } + + public synchronized void reloadConfigFile() throws IOException + { + try + { + LOCK.lock(); + + QpidLog4JConfigurator.configure(_log4jConfigFileName); + _logger.info("Applied log4j configuration from: " + _log4jConfigFileName); + } + catch (IllegalLoggerLevelException e) + { + _logger.warn("The log4j configuration reload request was aborted: " + e); + //recommended that MBeans should use standard java.* and javax.* exceptions only + throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); + } + catch (ParserConfigurationException e) + { + _logger.warn("The log4j configuration reload request was aborted: " + e); + throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); + } + catch (SAXException e) + { + _logger.warn("The log4j configuration reload request was aborted: " + e); + //recommended that MBeans should use standard java.* and javax.* exceptions only + throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); + } + catch (IOException e) + { + _logger.warn("The log4j configuration reload request was aborted: " + e); + throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); + } + finally + { + LOCK.unlock(); + } + } + + @Override + public ManagedObject getParentObject() + { + return null; + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/MBeanUtils.java new file mode 100644 index 0000000000..97e84d4796 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java new file mode 100644 index 0000000000..1416cfdd89 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/QueueMBean.java @@ -0,0 +1,663 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.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 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 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 position = 0; + + while(position < bodySize) + { + position += serverMsg.getContent(buf, position); + + } + + 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 getMessages(final long first, final long last) + { + final List messages = new ArrayList((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; + } + + + private 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java new file mode 100644 index 0000000000..597b98ccaa --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ServerInformationMBean.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.common.QpidProperties; +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 implements ServerInformation +{ + private String _buildVersion; + private String _productVersion; + + public ServerInformationMBean(ManagedObjectRegistry registry, Broker broker) + throws NotCompliantMBeanException, JMException + { + super(ServerInformation.class, ServerInformation.TYPE, registry, broker); + + _buildVersion = QpidProperties.getBuildVersion(); + _productVersion = QpidProperties.getReleaseVersion(); + + 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 _buildVersion; + } + + @Override + public String getProductVersion() throws IOException + { + return _productVersion; + } + + @Override + public boolean isStatisticsEnabled() + { + return false; + } + + @Override + public ManagedObject getParentObject() + { + // does not have a parent + return null; + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java new file mode 100644 index 0000000000..62733168ef --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/Shutdown.java @@ -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. + * + */ +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; +import java.util.Date; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Implementation of the JMX broker shutdown plugin. + */ +public class Shutdown extends DefaultManagedObject implements ShutdownMBean +{ + + private static final Logger _logger = Logger.getLogger(Shutdown.class); + + private static final String FORMAT = "yyyy/MM/dd HH:mm:ss"; + private static final int THREAD_COUNT = 1; + private static final ScheduledExecutorService EXECUTOR = new ScheduledThreadPoolExecutor(THREAD_COUNT); + + private final Runnable _shutdown = new SystemExiter(); + + public Shutdown(ManagedObjectRegistry registry) throws JMException + { + super(ShutdownMBean.class, ShutdownMBean.TYPE, registry); + register(); + } + + /** @see ShutdownMBean#shutdown() */ + public void shutdown() + { + _logger.info("Shutting down at user's request"); + shutdownBroker(0); + } + + /** @see ShutdownMBean#shutdown(long) */ + public void shutdown(final long delay) + { + if (delay < 0) + { + _logger.info("Shutting down at user's request"); + shutdownBroker(0); + } + else + { + _logger.info("Scheduled broker shutdown after " + delay + "ms"); + shutdownBroker(delay); + } + } + + /** @see ShutdownMBean#shutdownAt(String) */ + public void shutdownAt(final String when) + { + Date date; + DateFormat df = new SimpleDateFormat(FORMAT); + try + { + date = df.parse(when); + } + catch (ParseException e) + { + _logger.error("Invalid date \"" + when + "\": expecting " + FORMAT, e); + return; + } + _logger.info("Scheduled broker shutdown at " + when); + long now = System.currentTimeMillis(); + long time = date.getTime(); + if (time > now) + { + shutdownBroker(time - now); + } + else + { + shutdownBroker(0); + } + } + + /** + * Submits the {@link SystemExiter} job to shutdown the broker. + */ + private void shutdownBroker(long delay) + { + 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. + */ + class SystemExiter implements Runnable + { + public void run() + { + System.exit(0); + } + } + + /** + * @see ManagedObject#getObjectInstanceName() + */ + public String getObjectInstanceName() + { + return "Shutdown"; + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java new file mode 100644 index 0000000000..ed69c351f7 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/ShutdownMBean.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.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. + * + * Shuts the Qpid broker down via JMX. + */ +public interface ShutdownMBean +{ + static final String TYPE = "Shutdown"; + + /** + * Broker will be shut down immediately. + */ + @MBeanOperation(name="shutdown", description="Shut down immediately", impact = MBeanOperationInfo.ACTION) + public void shutdown(); + + /** + * Broker will be shutdown after the specified delay + * + * @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); + + /** + * Broker will be shutdown at the specified date and time. + * + * @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); +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBean.java new file mode 100644 index 0000000000..c7aade34b4 --- /dev/null +++ b/qpid/java/broker-plugins/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> 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/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java new file mode 100644 index 0000000000..85f53d9c0d --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.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.jmx.mbeans; + +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 final VirtualHost _virtualHost; + + private final Map _children = + new HashMap(); + 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 + { + // TODO + } + + } + catch(JMException e) + { + e.printStackTrace(); //TODO - report error on adding child MBean + } + } + } + + public void childRemoved(ConfiguredObject object, ConfiguredObject child) + { + synchronized (_children) + { + AMQManagedObject mbean = _children.remove(child); + if(mbean != null) + { + try + { + mbean.unregister(); + } + catch(JMException e) + { + e.printStackTrace(); //TODO - report error on removing child MBean + } + } + } + } + + @Override + public ManagedObject getParentObject() + { + return null; + } + + protected VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public Collection getQueues() + { + Collection children; + synchronized (_children) + { + children = new ArrayList(_children.values()); + } + Collection queues = new ArrayList(); + + for(AMQManagedObject child : children) + { + if(child instanceof QueueMBean) + { + queues.add((QueueMBean) child); + } + } + + return queues; + } +} diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java new file mode 100644 index 0000000000..9d12d8a493 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBean.java @@ -0,0 +1,240 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.AMQInvalidArgumentException; +import org.apache.qpid.AMQUnknownExchangeType; +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 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 exchangeTypes = _virtualHostMBean.getVirtualHost().getExchangeTypes(); + return exchangeTypes.toArray(new String[exchangeTypes.size()]); + } + + @Override + public List retrieveQueueAttributeNames() throws IOException + { + return ManagedQueue.QUEUE_ATTRIBUTES; + } + + @Override + public List> retrieveQueueAttributeValues( + @MBeanOperationParameter(name = "attributes", description = "Attributes to retrieve") String[] attributes) + throws IOException + { + int attributesLength = attributes.length; + + List> queueAttributesList = new ArrayList>(); + + for(QueueMBean queue : _virtualHostMBean.getQueues()) + { + + if(queue == null) + { + continue; + } + + List attributeValues = new ArrayList(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 originalArguments) + throws IOException, JMException + { + final Map 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 processNewQueueArguments(String queueName, + String owner, Map arguments) + { + final Map argumentsCopy; + if (_moveNonExclusiveQueueOwnerToDescription && owner != null) + { + argumentsCopy = new HashMap(arguments == null ? new HashMap() : 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java new file mode 100644 index 0000000000..a2631bab7f --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/NoopManagedObjectRegistry.java @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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; + +public class NoopManagedObjectRegistry implements ManagedObjectRegistry +{ + public NoopManagedObjectRegistry() + { + } + + public void start() + { + } + + public void registerObject(ManagedObject managedObject) throws JMException + { + } + + public void unregisterObject(ManagedObject managedObject) throws JMException + { + } + + public void close() + { + } +} diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java new file mode 100644 index 0000000000..f97c5a7210 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/ConnectionMBeanTest.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java new file mode 100644 index 0000000000..64b942c9a9 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.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.jmx.mbeans; + +import static org.mockito.Mockito.*; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +import org.apache.qpid.server.jmx.ManagedObjectRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; + +import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_LEVEL; +import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_NAME; + +import javax.management.JMException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularDataSupport; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class LoggingManagementMBeanTest extends InternalBrokerBaseCase +{ + + private static final String TEST_LOGGER = "LoggingManagementMBeanTestLogger"; + private static final String TEST_LOGGER_CHILD1 = "LoggingManagementMBeanTestLogger.child1"; + private static final String TEST_LOGGER_CHILD2 = "LoggingManagementMBeanTestLogger.child2"; + + private static final String TEST_CATEGORY_PRIORITY = "LogManMBeanTest.category.priority"; + private static final String TEST_CATEGORY_LEVEL = "LogManMBeanTest.category.level"; + private static final String TEST_LOGGER_LEVEL = "LogManMBeanTest.logger.level"; + + private static final String NEWLINE = System.getProperty("line.separator"); + + private File _testConfigFile; + + private ManagedObjectRegistry _registry = mock(ManagedObjectRegistry.class); + + @Override + public void setUp() throws Exception + { + super.setUp(); + _testConfigFile = createTempTestLog4JConfig(); + } + + @Override + public void tearDown() throws Exception + { + File oldTestConfigFile = new File(_testConfigFile.getAbsolutePath() + ".old"); + if(oldTestConfigFile.exists()) + { + oldTestConfigFile.delete(); + } + + _testConfigFile.delete(); + + super.tearDown(); + } + + private File createTempTestLog4JConfig() + { + File tmpFile = null; + try + { + tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp"); + tmpFile.deleteOnExit(); + + FileWriter fstream = new FileWriter(tmpFile); + BufferedWriter writer = new BufferedWriter(fstream); + + writer.write(""+NEWLINE); + writer.write(""+NEWLINE); + + writer.write(""+NEWLINE); + + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + + //Example of a 'category' with a 'priority' + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + + //Example of a 'category' with a 'level' + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + + //Example of a 'logger' with a 'level' + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + + //'root' logger + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + writer.write(" "+NEWLINE); + + writer.write(""+NEWLINE); + + writer.flush(); + writer.close(); + } + catch (IOException e) + { + fail("Unable to create temporary test log4j configuration"); + } + + return tmpFile; + } + + + + //******* Test Methods ******* // + + public void testSetRuntimeLoggerLevel() + { + LoggingManagementMBean lm = null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + //create a parent test logger, set its level explicitly + Logger log = Logger.getLogger(TEST_LOGGER); + log.setLevel(Level.toLevel("info")); + + //create child1 test logger, check its *effective* level is the same as the parent, "info" + Logger log1 = Logger.getLogger(TEST_LOGGER_CHILD1); + assertTrue("Test logger's level was not the expected value", + log1.getEffectiveLevel().toString().equalsIgnoreCase("info")); + + //now change its level to "warn" + assertTrue("Failed to set logger level", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "warn")); + + //check the change, see its actual level is "warn + assertTrue("Test logger's level was not the expected value", + log1.getLevel().toString().equalsIgnoreCase("warn")); + + //try an invalid level + assertFalse("Trying to set an invalid level succeded", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "made.up.level")); + } + + public void testSetRuntimeRootLoggerLevel() + { + LoggingManagementMBean lm = null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + Logger log = Logger.getRootLogger(); + + //get current root logger level + Level origLevel = log.getLevel(); + + //change level twice to ensure a new level is actually selected + + //set root loggers level to info + assertTrue("Failed to set root logger level", lm.setRuntimeRootLoggerLevel("debug")); + //check it is now actually info + Level currentLevel = log.getLevel(); + assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("debug"))); + + //try an invalid level + assertFalse("Trying to set an invalid level succeded", lm.setRuntimeRootLoggerLevel("made.up.level")); + + //set root loggers level to warn + assertTrue("Failed to set logger level", lm.setRuntimeRootLoggerLevel("info")); + //check it is now actually warn + currentLevel = log.getLevel(); + assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("info"))); + + //restore original level + log.setLevel(origLevel); + } + + public void testGetRuntimeRootLoggerLevel() + { + LoggingManagementMBean lm = null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + Logger log = Logger.getRootLogger(); + + //get current root logger level + Level origLevel = log.getLevel(); + + //change level twice to ensure a new level is actually selected + + //set root loggers level to debug + log.setLevel(Level.toLevel("debug")); + //check it is now actually debug + assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("debug")); + + + //set root loggers level to warn + log.setLevel(Level.toLevel("info")); + //check it is now actually warn + assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("info")); + + //restore original level + log.setLevel(origLevel); + } + + public void testViewEffectiveRuntimeLoggerLevels() + { + LoggingManagementMBean lm = null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + //(re)create a parent test logger, set its level explicitly + Logger log = Logger.getLogger(TEST_LOGGER); + log.setLevel(Level.toLevel("info")); + + //retrieve the current effective runtime logger level values + TabularDataSupport levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); + Collection records = levels.values(); + Map list = new HashMap(); + for (Object o : records) + { + CompositeData data = (CompositeData) o; + list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); + } + + //check child2 does not exist already + assertFalse("Did not expect this logger to exist already", list.containsKey(TEST_LOGGER_CHILD2)); + + //create child2 test logger + Logger log2 = Logger.getLogger(TEST_LOGGER_CHILD2); + + //retrieve the current effective runtime logger level values + levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); + records = levels.values(); + list = new HashMap(); + for (Object o : records) + { + CompositeData data = (CompositeData) o; + list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); + } + + //verify the parent and child2 loggers are present in returned values + assertTrue(TEST_LOGGER + " logger was not in the returned list", list.containsKey(TEST_LOGGER)); + assertTrue(TEST_LOGGER_CHILD2 + " logger was not in the returned list", list.containsKey(TEST_LOGGER_CHILD2)); + + //check child2's effective level is the same as the parent, "info" + assertTrue("Test logger's level was not the expected value", + list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("info")); + + //now change its level explicitly to "warn" + log2.setLevel(Level.toLevel("warn")); + + //retrieve the current effective runtime logger level values + levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); + records = levels.values(); + list = new HashMap(); + for (Object o : records) + { + CompositeData data = (CompositeData) o; + list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); + } + + //check child2's effective level is now "warn" + assertTrue("Test logger's level was not the expected value", + list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("warn")); + } + + public void testViewAndSetConfigFileLoggerLevel() throws Exception + { + LoggingManagementMBean lm =null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + //retrieve the current values + TabularDataSupport levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels(); + Collection records = levels.values(); + Map list = new HashMap(); + for (Object o : records) + { + CompositeData data = (CompositeData) o; + list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); + } + + //check the 3 different types of logger definition are successfully retrieved before update + assertTrue("Wrong number of items in returned list", list.size() == 3); + assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY)); + assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL)); + assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL)); + + //check that their level is as expected + assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("info")); + assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("warn")); + assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("error")); + + //increase their levels a notch to test the 3 different types of logger definition are successfully updated + //change the category+priority to warn + assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_PRIORITY, "warn")); + //change the category+level to error + assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_LEVEL, "error")); + //change the logger+level to trace + assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "trace")); + + //try an invalid level + assertFalse("Use of an invalid logger level was successfull", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "made.up.level")); + + //try an invalid logger name + assertFalse("Use of an invalid logger name was successfull", lm.setConfigFileLoggerLevel("made.up.logger.name", "info")); + + //retrieve the new values from the file and check them + levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels(); + records = levels.values(); + list = new HashMap(); + for (Object o : records) + { + CompositeData data = (CompositeData) o; + list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); + } + + //check the 3 different types of logger definition are successfully retrieved after update + assertTrue("Wrong number of items in returned list", list.size() == 3); + assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY)); + assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL)); + assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL)); + + //check that their level is as expected after the changes + assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("warn")); + assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("error")); + assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("trace")); + } + + public void testGetAndSetConfigFileRootLoggerLevel() throws Exception + { + LoggingManagementMBean lm =null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + //retrieve the current value + String level = lm.getConfigFileRootLoggerLevel(); + + //check the value was successfully retrieved before update + assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("info")); + + //try an invalid level + assertFalse("Use of an invalid RootLogger level was successfull", lm.setConfigFileRootLoggerLevel("made.up.level")); + + //change the level to warn + assertTrue("Failed to set new RootLogger level", lm.setConfigFileRootLoggerLevel("warn")); + + //retrieve the current value + level = lm.getConfigFileRootLoggerLevel(); + + //check the value was successfully retrieved after update + assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("warn")); + } + + public void testGetLog4jLogWatchInterval() + { + LoggingManagementMBean lm =null; + try + { + lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 5000, _registry); + } + catch (JMException e) + { + fail("Could not create test LoggingManagementMBean"); + } + + assertTrue("Wrong value returned for logWatch period", lm.getLog4jLogWatchInterval() == 5000); + } + +} diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/MBeanTestUtils.java new file mode 100644 index 0000000000..5f913e5f33 --- /dev/null +++ b/qpid/java/broker-plugins/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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java new file mode 100644 index 0000000000..2003c12735 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/QueueMBeanTest.java @@ -0,0 +1,368 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.Matchers.isNull; +import static org.mockito.Matchers.argThat; + +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 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.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 QueueMBeanTest extends TestCase +{ + 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 + { + _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() + { + @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); + } + +} diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/UserManagementMBeanTest.java new file mode 100644 index 0000000000..8ca6c521eb --- /dev/null +++ b/qpid/java/broker-plugins/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 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java new file mode 100644 index 0000000000..93a80665a9 --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/VirtualHostManagerMBeanTest.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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; +import org.mockito.verification.VerificationMode; + +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 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 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 arguments = Collections.singletonMap(AMQQueueFactory.X_QPID_DESCRIPTION, (Object)TEST_DESCRIPTION); + _virtualHostManagerMBean.createNewQueue(TEST_QUEUE_NAME, TEST_OWNER, true, arguments); + + Map 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java new file mode 100644 index 0000000000..2c341b7f2e --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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; + +import javax.management.MBeanException; +import javax.management.ObjectName; + +/** + * 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 (MBeanException mbe) + { + // PASS + assertEquals("Error in unregistering exchange " + defaultExchangeName, mbe.getMessage()); + assertTrue(mbe.getCause().getMessage().contains("Cannot unregister the default exchange")); + } + final ManagedExchange defaultExchange = _jmxUtils.getManagedExchange(defaultExchangeName); + assertNotNull("Exchange should exist", defaultExchange); + } + +} diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java new file mode 100644 index 0000000000..28d7bf4aed --- /dev/null +++ b/qpid/java/broker-plugins/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 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 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 rowItr = (Iterator) 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java new file mode 100644 index 0000000000..47b38381c5 --- /dev/null +++ b/qpid/java/broker-plugins/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: + * + * 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 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: Name: + * + * 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 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 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 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 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: + * + * QUE-1001 : Create : Transient Owner: + * + * 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 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: + * + * 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 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: + * + * 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 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 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 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: + * + * 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 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java new file mode 100644 index 0000000000..6100d5a23e --- /dev/null +++ b/qpid/java/broker-plugins/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 : : Listening on port + * MNG-1003 : Shutting down : : port + * MNG-1004 : Ready + * MNG-1005 : Stopped + * MNG-1006 : Using SSL Keystore : + * MNG-1007 : Open : User + * MNG-1008 : Close : User + */ +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: + * + * 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 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 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: + * + * 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: + * + * 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 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: + * + * 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 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: + * + * MESSAGE MNG-1007 : Open : User + * MESSAGE MNG-1008 : Close : User + * + * 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 openResults = null; + List 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java new file mode 100644 index 0000000000..ad6777d0ea --- /dev/null +++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java @@ -0,0 +1,609 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.management.Notification; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +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"; + + _connection = getConnection(); + _connection.start(); + + _session = _connection.createSession(true, Session.SESSION_TRANSACTED); + _sourceQueue = _session.createQueue(_sourceQueueName); + _destinationQueue = _session.createQueue(_destinationQueueName); + createQueueOnBroker(_sourceQueue); + createQueueOnBroker(_destinationQueue); + + _jmxUtils.open(); + + _managedSourceQueue = _jmxUtils.getManagedQueue(_sourceQueueName); + _managedDestinationQueue = _jmxUtils.getManagedQueue(_destinationQueueName); + } + + 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 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 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 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 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 = "amq.fanout"; + 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 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 rowItr = (Iterator) 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 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 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 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 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 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()); + } + + 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) + { + Message message = consumer.receive(1000); + assertNotNull("Expected message with index " + i, message); + assertEquals("Expected message with index " + i, i, message.getIntProperty(INDEX)); + } + + assertNull("Unexpected message encountered", consumer.receive(1000)); + } + + private List getAMQMessageIdsOn(ManagedQueue managedQueue, long startIndex, long endIndex) throws Exception + { + final SortedSet messageIds = new TreeSet(); + + final TabularData tab = managedQueue.viewMessages(startIndex, endIndex); + final Iterator rowItr = (Iterator) 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(messageIds); + } + + /** + * + * Utility method to convert array of Strings in the form x = y into a + * map with key/value x => y. + * + */ + private Map headerArrayToMap(final String[] headerArray) + { + final Map headerMap = new HashMap(); + final List headerList = Arrays.asList(headerArray); + for (Iterator 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 final class RecordingNotificationListener implements NotificationListener + { + private final CountDownLatch _notificationReceivedLatch; + private final AtomicInteger _numberOfNotifications; + private final AtomicReference _lastNotification; + + private RecordingNotificationListener(int expectedNumberOfNotifications) + { + _notificationReceivedLatch = new CountDownLatch(expectedNumberOfNotifications); + _numberOfNotifications = new AtomicInteger(0); + _lastNotification = new AtomicReference(); + } + + @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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java new file mode 100644 index 0000000000..c3fff94923 --- /dev/null +++ b/qpid/java/broker-plugins/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 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java new file mode 100644 index 0000000000..62b1b554a9 --- /dev/null +++ b/qpid/java/broker-plugins/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 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/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/UserManagementWithBase64MD5PasswordsTest.java new file mode 100644 index 0000000000..84a66232ce --- /dev/null +++ b/qpid/java/broker-plugins/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 getPrincipalDatabaseImplClass() + { + return Base64MD5PasswordFilePrincipalDatabase.class; + } + +} diff --git a/qpid/java/broker-plugins/management/MANIFEST.MF b/qpid/java/broker-plugins/management/MANIFEST.MF new file mode 100644 index 0000000000..d817ac5302 --- /dev/null +++ b/qpid/java/broker-plugins/management/MANIFEST.MF @@ -0,0 +1,66 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Qpid Broker-Plugins Management +Bundle-SymbolicName: broker-plugins-management +Bundle-Description: 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.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.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.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/qpid/java/broker-plugins/management/build.xml b/qpid/java/broker-plugins/management/build.xml new file mode 100644 index 0000000000..4a28ff2659 --- /dev/null +++ b/qpid/java/broker-plugins/management/build.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/Management.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/Management.java new file mode 100644 index 0000000000..589f46749d --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/Management.java @@ -0,0 +1,155 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collection; +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.*; +import org.apache.qpid.server.model.*; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +public class Management +{ + + private final Logger _logger = Logger.getLogger(Management.class); + + private Broker _broker; + + private Collection _servers = new ArrayList(); + + + public Management() + { + _broker = ApplicationRegistry.getInstance().getBroker(); + + Collection ports = _broker.getPorts(); + for(Port port : ports) + { + // TODO - cover cases where more than just HTTP supported, and SSL as a transport + if(port.getProtocols().contains(Protocol.HTTP)) + { + if(port.getTransports().contains(Transport.TCP)) + { + int portNumber = port.getPort(); + if (_logger.isInfoEnabled()) + { + _logger.info("Creating web server on port " + portNumber); + } + _servers.add(createServer(portNumber)); + } + } + } + + if (_logger.isDebugEnabled()) + { + _logger.info(_servers.size() + " server(s) defined"); + } + + } + + private Server createServer(int port) + { + _logger.info("Starting up web server on port " + port); + + Server server = new Server(port); + SocketAddress socketAddress = new InetSocketAddress(port); + + 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", socketAddress); + addRestServlet(root, "virtualhost", socketAddress, VirtualHost.class); + addRestServlet(root, "authenticationprovider", socketAddress, AuthenticationProvider.class); + addRestServlet(root, "user", socketAddress, AuthenticationProvider.class, User.class); + addRestServlet(root, "exchange", socketAddress, VirtualHost.class, Exchange.class); + addRestServlet(root, "queue", socketAddress, VirtualHost.class, Queue.class); + addRestServlet(root, "connection", socketAddress, VirtualHost.class, Connection.class); + addRestServlet(root, "binding", socketAddress, VirtualHost.class, Exchange.class, Queue.class, Binding.class); + addRestServlet(root, "port", socketAddress, Port.class); + addRestServlet(root, "session", socketAddress, VirtualHost.class, Connection.class, Session.class); + + root.addServlet(new ServletHolder(new StructureServlet(_broker, socketAddress)), "/rest/structure"); + root.addServlet(new ServletHolder(new MessageServlet(_broker, socketAddress)), "/rest/message/*"); + root.addServlet(new ServletHolder(new MessageContentServlet(_broker, socketAddress)), "/rest/message-content/*"); + + root.addServlet(new ServletHolder(new LogRecordsServlet(_broker, socketAddress)), "/rest/logrecords"); + + + root.addServlet(new ServletHolder(new SaslServlet(_broker, socketAddress)), "/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, SocketAddress socketAddress, Class... hierarchy) + { + root.addServlet(new ServletHolder(new RestServlet(_broker, socketAddress, 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(); + } + } + +} diff --git a/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java new file mode 100644 index 0000000000..2600d8a7bf --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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()) + { + _logger.info("Management plugin is diabled!"); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java new file mode 100644 index 0000000000..3866da8f89 --- /dev/null +++ b/qpid/java/broker-plugins/management/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 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java new file mode 100644 index 0000000000..d66555787f --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java @@ -0,0 +1,79 @@ +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; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

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

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java new file mode 100644 index 0000000000..f8ca082d79 --- /dev/null +++ b/qpid/java/broker-plugins/management/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 CONTENT_TYPES; + + static + { + + Map contentTypes = new HashMap(); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java new file mode 100644 index 0000000000..a3c5ec68a2 --- /dev/null +++ b/qpid/java/broker-plugins/management/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 vhosts = _broker.getVirtualHosts(); + Collection exchanges = new ArrayList(); + Collection> outputObject = new ArrayList>(); + + 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 convertToObject(final Exchange exchange) + { + Map object = new LinkedHashMap(); + 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 arguments = new HashMap(); + 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 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 attributes = new HashMap(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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java new file mode 100644 index 0000000000..b2c0fcfe52 --- /dev/null +++ b/qpid/java/broker-plugins/management/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 vhosts = _broker.getVirtualHosts(); + + + + final PrintWriter writer = response.getWriter(); + + ObjectMapper mapper = new ObjectMapper(); + + if(request.getPathInfo() == null || request.getPathInfo().length()==0) + { + + LinkedHashMap vhostObject = new LinkedHashMap(); + List vhostList = new ArrayList(); + + for(VirtualHost vhost : vhosts) + { + vhostList.add(Collections.singletonMap("name", vhost.getName())); + } + mapper.writeValue(writer, vhostList); + } + else + { + LinkedHashMap vhostObject = new LinkedHashMap(); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java new file mode 100644 index 0000000000..123f352ec1 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java @@ -0,0 +1,215 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.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; + private SocketAddress _socketAddress; + + protected AbstractServlet() + { + super(); + _broker = ApplicationRegistry.getInstance().getBroker(); + _socketAddress = null; + } + + protected AbstractServlet(Broker broker, SocketAddress socketAddress) + { + _broker = broker; + _socketAddress = socketAddress; + } + + @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) + { + if (_socketAddress == null) + { + return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort()); + } + return _socketAddress; + } +} diff --git a/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/KeyComparator.java new file mode 100644 index 0000000000..3d862ce321 --- /dev/null +++ b/qpid/java/broker-plugins/management/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 +{ + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java new file mode 100644 index 0000000000..7a4b92f907 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.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.io.PrintWriter; +import java.net.SocketAddress; +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(), null); + } + + public LogRecordsServlet(Broker broker, SocketAddress socketaddress) + { + super(broker, socketaddress); + } + + @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> logRecords = new ArrayList>(); + + 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 logRecordToObject(LogRecorder.Record record) + { + Map recordMap = new LinkedHashMap(); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MapComparator.java new file mode 100644 index 0000000000..84d987813b --- /dev/null +++ b/qpid/java/broker-plugins/management/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 +{ + private Comparator[] _sortKeys; + + public MapComparator(final String[] sortKeys) + { + _sortKeys = parseKeys(sortKeys); + } + + private static Comparator[] parseKeys(final String[] sortKeys) + { + Comparator[] 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java new file mode 100644 index 0000000000..4d58a9f3b0 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; +import java.net.SocketAddress; +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, SocketAddress socketaddress) + { + super(broker, socketaddress); + } + + @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 names = new ArrayList(); + // 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java new file mode 100644 index 0000000000..b47dc8b28e --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -0,0 +1,503 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.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, SocketAddress socketaddress) + { + super(broker, socketaddress); + } + + @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> 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 names = new ArrayList(); + // 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 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 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 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> _messages = new ArrayList>(); + + 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 messageObject = convertToObject(entry, false); + messageObject.put("position", _position); + _messages.add(messageObject); + } + return _last != -1 && _position > _last; + } + + public List> getMessages() + { + return _messages; + } + } + + + private class MessageFinder implements QueueEntryVisitor + { + private final long _messageNumber; + private Map _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 getMessageObject() + { + return _messageObject; + } + } + + private Map convertToObject(QueueEntry entry, boolean includeContent) + { + Map object = new LinkedHashMap(); + 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 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 object, String name, Object property) + { + if(property != null) + { + object.put(name, property); + } + } + + private void convertMessageHeaders(Map object, ServerMessage message) + { + final AMQMessageHeader messageHeader = message.getMessageHeader(); + if(messageHeader != null) + { + Map headers = new HashMap(); + 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 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 messageIds = new ArrayList(); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java new file mode 100644 index 0000000000..96e260da56 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -0,0 +1,576 @@ +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.qpid.AMQSecurityException; +import org.apache.qpid.server.model.*; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

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

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class RestServlet extends AbstractServlet +{ + /** + * 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 RESERVED_PARAMS = new HashSet(Arrays.asList(DEPTH_PARAM, SORT_PARAM)); + + private Class[] _hierarchy; + + private volatile boolean initializationRequired = false; + + public RestServlet() + { + super(); + initializationRequired = true; + } + + public RestServlet(Broker broker, SocketAddress socketaddress, Class... hierarchy) + { + super(broker, socketaddress); + _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> classes = new ArrayList>(); + 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 clazz = (Class)itemClass; + classes.add(clazz); + } + Class[] hierachyClasses = (Class[])new Class[classes.size()]; + _hierarchy = classes.toArray(hierachyClasses); + } + else + { + _hierarchy = (Class[])new Class[0]; + } + } + + protected Collection getObjects(HttpServletRequest request) + { + List names = new ArrayList(); + 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 parents = Collections.singleton((ConfiguredObject) getBroker()); + Collection children = new ArrayList(); + + Map, String> filters = + new HashMap, 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(); + } + + if(!filters.isEmpty()) + { + Collection potentials = parents; + parents = new ArrayList(); + + for(ConfiguredObject o : potentials) + { + + boolean match = true; + + for(Map.Entry, String> entry : filters.entrySet()) + { + Collection 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 filter(Collection objects, HttpServletRequest request) + { + + + Map> filters = new HashMap>(); + + for(String param : (Collection) Collections.list(request.getParameterNames())) + { + if(!RESERVED_PARAMS.contains(param)) + { + filters.put(param, Arrays.asList(request.getParameterValues(param))); + } + } + + if(filters.isEmpty()) + { + return objects; + } + + Collection filteredObj = new ArrayList(objects); + + Iterator iter = filteredObj.iterator(); + + while(iter.hasNext()) + { + ConfiguredObject obj = iter.next(); + for(Map.Entry> entry : filters.entrySet()) + { + Object value = obj.getAttribute(entry.getKey()); + if(!entry.getValue().contains(String.valueOf(value))) + { + iter.remove(); + } + } + + } + + return filteredObj; + } + + private Collection getAncestors(Class childType, + Class ancestorType, + ConfiguredObject child) + { + Collection ancestors = new HashSet(); + Collection> parentTypes = Model.getParentTypes(childType); + + for(Class 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 convertObjectToMap(final ConfiguredObject confObject, + Class clazz, + int depth) + { + Map object = new LinkedHashMap(); + + for(String name : confObject.getAttributeNames()) + { + Object value = confObject.getAttribute(name); + if(value != null) + { + object.put(name, value); + } + } + + Statistics statistics = confObject.getStatistics(); + Map statMap = new HashMap(); + 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 childClass : Model.getChildTypes(clazz)) + { + Collection children = confObject.getChildren(childClass); + if(children != null) + { + List> childObjects = new ArrayList>(); + + 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 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> output = new ArrayList>(); + + // 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 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 providedObject = mapper.readValue(request.getInputStream(), LinkedHashMap.class); + + + List names = new ArrayList(); + 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[] 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(); + 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 parents = new ArrayList(); + Class objClass = getConfiguredClass(); + Collection> 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 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 providedObject) + { + for(Map.Entry 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 + { + // 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 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java new file mode 100644 index 0000000000..4ca2d270dd --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -0,0 +1,253 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.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.net.SocketAddress; +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 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, SocketAddress socketaddress) + { + super(broker, socketaddress); + } + + 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 outputObject = new LinkedHashMap(); + final Subject subject = (Subject) session.getAttribute("subject"); + if(subject != null) + { + final Principal principal = subject.getPrincipals().iterator().next(); + outputObject.put("user", principal.getName()); + } + + 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) + { + //TODO + e.printStackTrace(); + throw e; + } + catch(RuntimeException e) + { + //TODO + e.printStackTrace(); + 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 outputObject = new LinkedHashMap(); + 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/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java new file mode 100644 index 0000000000..6295d74b42 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java @@ -0,0 +1,104 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

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

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.SocketAddress; +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, SocketAddress socketaddress) + { + super(broker, socketaddress); + } + + @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 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 generateStructure(ConfiguredObject object, Class clazz) + { + Map structure = new LinkedHashMap(); + structure.put("id", object.getId()); + structure.put("name", object.getName()); + + for(Class childClass : Model.getChildTypes(clazz)) + { + Collection children = object.getChildren(childClass); + if(children != null) + { + List> childObjects = new ArrayList>(); + + for(ConfiguredObject child : children) + { + childObjects.add(generateStructure(child, childClass)); + } + + if(!childObjects.isEmpty()) + { + structure.put(pluralize(childClass),childObjects); + } + } + } + + return structure; + } + + private String pluralize(Class childClass) + { + String name = childClass.getSimpleName().toLowerCase(); + return name + (name.endsWith("s") ? "es" : "s"); + } +} diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/addBinding.html b/qpid/java/broker-plugins/management/src/main/java/resources/addBinding.html new file mode 100644 index 0000000000..8dbd219c8d --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/addBinding.html @@ -0,0 +1,42 @@ + +

+
+
+ + + + + + + + + + + + + +
Exchange Name*:
Queue Name*:
Binding Key*:
+
+ + + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/addExchange.html b/qpid/java/broker-plugins/management/src/main/java/resources/addExchange.html new file mode 100644 index 0000000000..cccdfbd00c --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/addExchange.html @@ -0,0 +1,53 @@ + +
+
+
+ + + + + + + + + + + + + +
Exchange Name*:
Durable?
Exchange Type: + +
+
+ + + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/addQueue.html b/qpid/java/broker-plugins/management/src/main/java/resources/addQueue.html new file mode 100644 index 0000000000..8154f053cd --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/addQueue.html @@ -0,0 +1,174 @@ + +
+
+
+ + + + + + + + + + + + + +
Queue Name*:
Durable?
Queue Type: + + +    + + +    + + +    + + +
+
+ + + + + + + + +
+
+ + + + + + + + + + + + +
Capacity:
Resume Capacity:
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
Maximum Message Age:
Maximum Message Size:
Maximum Number in Queue:
Gap between alerts:
+
+
+
+ + + + + + + + + +
Maximum Delivery Retries:
+
+
+ + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/addUser.html b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/addUser.html new file mode 100644 index 0000000000..785605f694 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/addUser.html @@ -0,0 +1,42 @@ + +
+
+
+ + + + + + + + + +
User Name*:
Password*
+
+ + + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/setPassword.html b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/setPassword.html new file mode 100644 index 0000000000..3d67463abd --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/setPassword.html @@ -0,0 +1,42 @@ + +
+
+
+ + + + + + + + + +
User Name:
Password*
+
+ + + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html new file mode 100644 index 0000000000..baadc8c35f --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html @@ -0,0 +1,29 @@ + +
+
+
+ + + +
+ +
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/css/common.css b/qpid/java/broker-plugins/management/src/main/java/resources/css/common.css new file mode 100644 index 0000000000..78780edcd9 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/footer.html b/qpid/java/broker-plugins/management/src/main/java/resources/footer.html new file mode 100644 index 0000000000..fa84825e80 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/footer.html @@ -0,0 +1,28 @@ + + + \ No newline at end of file diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/authorization/sasl.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/authorization/sasl.js new file mode 100644 index 0000000000..152504da86 --- /dev/null +++ b/qpid/java/broker-plugins/management/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>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: + '' + + '

' + + '' + + '

' + + '' + }); + + button = new DropDownButton({ + label: "Login", + dropDown: dialog + }); + + usernameSpan = domConstruct.create("span", { innerHTML: 'User: ', + 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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/UpdatableStore.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/UpdatableStore.js new file mode 100644 index 0000000000..f7ede1a7f7 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/footer.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/footer.js new file mode 100644 index 0000000000..ea13b1fc53 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/formatter.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/formatter.js new file mode 100644 index 0000000000..2f8683ee1c --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/properties.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/properties.js new file mode 100644 index 0000000000..8d85345b74 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/updater.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/updater.js new file mode 100644 index 0000000000..86bbaa46ba --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/util.js new file mode 100644 index 0000000000..d7917b640e --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/common/util.js @@ -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. + * + */ +define([], + function () { + 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] ); + } + } + } + } + }; + return util; + }); \ No newline at end of file diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/AuthenticationProvider.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/AuthenticationProvider.js new file mode 100644 index 0000000000..7613fd5d71 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Broker.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Broker.js new file mode 100644 index 0000000000..dcf6711073 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Connection.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Connection.js new file mode 100644 index 0000000000..01f9a325c5 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Exchange.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Exchange.js new file mode 100644 index 0000000000..0450ef53ac --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Exchange.js @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +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", + "dojo/domReady!"], + function (xhr, parser, query, connect, registry, properties, updater, util, formatter, UpdatableStore, addBinding) { + + 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()}); + }); + + }}); + }; + + Exchange.prototype.close = function() { + updater.remove( this.exchangeUpdater ); + }; + + 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%"} + ]); + + }); + + } + + 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) + + }); + }; + + + return Exchange; + }); diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Queue.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Queue.js new file mode 100644 index 0000000000..1eb0bfdd79 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/Queue.js @@ -0,0 +1,438 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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()}); + }); + + }}); + + + + }; + + 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"; + tableStr += ""+encode(val[ name ])+""; + } + field.innerHTML = tableStr; + } + tableStr += ""; + } 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 = "Download"; + } + 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/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/treeView.js b/qpid/java/broker-plugins/management/src/main/java/resources/js/qpid/management/treeView.js new file mode 100644 index 0000000000..b1d4abf8c1 --- /dev/null +++ b/qpid/java/broker-plugins/management/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/qpid/java/broker-plugins/management/src/main/java/resources/management.html b/qpid/java/broker-plugins/management/src/main/java/resources/management.html new file mode 100644 index 0000000000..a8345a8503 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/management.html @@ -0,0 +1,92 @@ + + + + + + Qpid Management + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/moveCopyMessages.html b/qpid/java/broker-plugins/management/src/main/java/resources/moveCopyMessages.html new file mode 100644 index 0000000000..f188c3001c --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/moveCopyMessages.html @@ -0,0 +1,36 @@ + + +
+
+
+ + + + + +
Queue:
+
+ + + + + + +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showAuthProvider.html b/qpid/java/broker-plugins/management/src/main/java/resources/showAuthProvider.html new file mode 100644 index 0000000000..c5d4e48a75 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showAuthProvider.html @@ -0,0 +1,25 @@ + +
+ Name: +
+ Type: +
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showBroker.html b/qpid/java/broker-plugins/management/src/main/java/resources/showBroker.html new file mode 100644 index 0000000000..cb2f4a4b9a --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showBroker.html @@ -0,0 +1,25 @@ +
+ Broker: +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showConnection.html b/qpid/java/broker-plugins/management/src/main/java/resources/showConnection.html new file mode 100644 index 0000000000..84854daf47 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showConnection.html @@ -0,0 +1,47 @@ + +
+ Name: +
+ State: + Pre-fetched: +
+ Durable: + Inbound: + + msg/s + + +
+ Lifespan: + Outbound: + + msg/s + + +
+
+
+
+
+
+ +
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showExchange.html b/qpid/java/broker-plugins/management/src/main/java/resources/showExchange.html new file mode 100644 index 0000000000..64f351d218 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showExchange.html @@ -0,0 +1,46 @@ + +
+ Exchange: +
+ State: +
+ Durable: + Inbound: + + msg/s + + +
+ Lifespan: + Dropped: + + msg/s + + +
+
+
+
+ +
+
+
diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showMessage.html b/qpid/java/broker-plugins/management/src/main/java/resources/showMessage.html new file mode 100644 index 0000000000..0dea508c60 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showMessage.html @@ -0,0 +1,73 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message Number:
Message Id:
State:
Persistent:
Priority:
Arrival Time: +
Expiration:
MIME Type:
User:
Headers:
Content:
+
+ + +
+
+ diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showQueue.html b/qpid/java/broker-plugins/management/src/main/java/resources/showQueue.html new file mode 100644 index 0000000000..c87a462760 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showQueue.html @@ -0,0 +1,97 @@ + +
+ Queue: + Size: + + msgs + ( + ) +
+ State: + Pre-fetched: + + msgs + + +
+ Durable: + Inbound: + + msg/s + + +
+ Lifespan: + Outbound: + + msg/s + + +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + +
+
+
+ Max. Queue Size: + + msgs + + + +
+ Max. Message Age: + + + + Size: + + +
+
+ Alert frequency: + + + + + +
+
+ diff --git a/qpid/java/broker-plugins/management/src/main/java/resources/showVirtualHost.html b/qpid/java/broker-plugins/management/src/main/java/resources/showVirtualHost.html new file mode 100644 index 0000000000..9d16d523d6 --- /dev/null +++ b/qpid/java/broker-plugins/management/src/main/java/resources/showVirtualHost.html @@ -0,0 +1,84 @@ + + + +
+ Name: +
+ State: +
+ Durable: + Inbound: + + msg/s + + +
+ Lifespan: + Outbound: + + msg/s + + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ Max. Queue Size: + + msgs + + + +
+ Max. Message Age: + + + + Size: + + +
+
+ Alert frequency: + + +
+
+ diff --git a/qpid/java/broker/etc/broker_example.acl b/qpid/java/broker/etc/broker_example.acl index aae4ee3162..1f32f8463e 100644 --- a/qpid/java/broker/etc/broker_example.acl +++ b/qpid/java/broker/etc/broker_example.acl @@ -24,6 +24,8 @@ #Define a 'messaging-users' group with users 'client' and 'server' in it GROUP messaging-users client server +#Define a group for management web console users +GROUP webadmins webadmin ### MANAGEMENT #### @@ -74,6 +76,23 @@ ACL ALLOW-LOG server CONSUME QUEUE name="example.RequestQueue" ACL ALLOW-LOG server BIND EXCHANGE ACL ALLOW-LOG server PUBLISH EXCHANGE name="amq.direct" routingKey="TempQueue*" +# ACL for web management console admins +# All rules below are required for console admin users +# to perform create/update/delete operations +ACL ALLOW-LOG webadmins CREATE QUEUE +ACL ALLOW-LOG webadmins DELETE QUEUE +ACL ALLOW-LOG webadmins PURGE QUEUE +ACL ALLOW-LOG webadmins CREATE EXCHANGE +ACL ALLOW-LOG webadmins DELETE EXCHANGE +ACL ALLOW-LOG webadmins BIND EXCHANGE +ACL ALLOW-LOG webadmins UNBIND EXCHANGE +ACL ALLOW-LOG webadmins UPDATE METHOD + +# at the moment only the following UPDATE METHOD rules are supported by web management console +#ACL ALLOW-LOG webadmins UPDATE METHOD component="VirtualHost.Queue" name="moveMessages" +#ACL ALLOW-LOG webadmins UPDATE METHOD component="VirtualHost.Queue" name="copyMessages" +#ACL ALLOW-LOG webadmins UPDATE METHOD component="VirtualHost.Queue" name="deleteMessages" + ### DEFAULT ### #Deny all users from performing all operations diff --git a/qpid/java/broker/etc/log4j.xml b/qpid/java/broker/etc/log4j.xml index 7dbb1bc87d..b881539a52 100644 --- a/qpid/java/broker/etc/log4j.xml +++ b/qpid/java/broker/etc/log4j.xml @@ -87,10 +87,6 @@ - - - - @@ -101,8 +97,6 @@ - - diff --git a/qpid/java/broker/etc/passwd b/qpid/java/broker/etc/passwd index 99f0f05c6a..f0dcb80f25 100644 --- a/qpid/java/broker/etc/passwd +++ b/qpid/java/broker/etc/passwd @@ -21,3 +21,5 @@ client:guest server:guest admin:admin +webadmin:webadmin + diff --git a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java index dca62f34b4..79bedb2a7e 100644 --- a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java +++ b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java @@ -26,8 +26,6 @@ import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; -import org.apache.qpid.server.logging.management.LoggingManagementMBean; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -65,7 +63,6 @@ public class QpidLog4JConfigurator LOCK.lock(); parseXMLConfigFile(filename); - checkLoggerLevels(filename); DOMConfigurator.configure(filename); @@ -84,7 +81,6 @@ public class QpidLog4JConfigurator SAXException, IllegalLoggerLevelException { parseXMLConfigFile(filename); - checkLoggerLevels(filename); QpidLog4JXMLWatchdog watchdog = new QpidLog4JXMLWatchdog(filename); watchdog.setDelay(delay); @@ -200,23 +196,6 @@ public class QpidLog4JConfigurator return; } - try - { - checkLoggerLevels(filename); - } - catch (Exception e) - { - //logger will be instantiated following first configuration success, which has been pre-validated - //and so the null check should never actually be required. - if(_logger != null) - { - _logger.warn("Errors were found when validating the logger level values in the " + - "log4j XML configuration file. The new configuration was not applied. " + - "Correct the issues to prompt another update attempt: " + e.getMessage()); - } - return; - } - //everything checked was ok, let the normal update process proceed super.doOnChange(); @@ -235,60 +214,7 @@ public class QpidLog4JConfigurator } } - - protected static void checkLoggerLevels(String filename) throws IllegalLoggerLevelException, IOException - { - //check that the logger levels specified in the XML are actually valid - try - { - LOCK.lock(); - - //get the Logger levels to check - Map loggersLevels; - loggersLevels = LoggingManagementMBean.retrieveConfigFileLoggersLevels(filename); - //add the RootLogger to the list too - String rootLoggerlevelString = LoggingManagementMBean.retrieveConfigFileRootLoggerLevel(filename); - loggersLevels.put("Root", rootLoggerlevelString); - - - for (Map.Entry entry : loggersLevels.entrySet()) - { - String loggerName = entry.getKey(); - String levelString = entry.getValue(); - - //let log4j replace any properties in the string - String log4jConfiguredString = domConfig.subst(levelString); - - if(log4jConfiguredString.equals("") && ! log4jConfiguredString.equals(levelString)) - { - //log4j has returned an empty string but this isnt what we gave it. - //There may have been an undefined property. Unlike an incorrect - //literal value, we will allow this case to proceed, but warn users. - - if(_logger != null) - { - _logger.warn("Unable to detect Level value from '" + levelString - +"' for logger '" + loggerName + "', Log4J will default this to DEBUG"); - } - else - { - System.err.println("Unable to detect Level value from '" + levelString - +"' for logger " + loggerName + ", Log4J will default this to DEBUG"); - } - - continue; - } - - checkLevel(loggerName,log4jConfiguredString); - } - } - finally - { - LOCK.unlock(); - } - } - private static void checkLevel(String loggerName, String levelString) throws IllegalLoggerLevelException { if("null".equalsIgnoreCase(levelString) || "inherited".equalsIgnoreCase(levelString)) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java index 034a4ae53c..0d48aec562 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java @@ -540,6 +540,11 @@ public class ManagementExchange implements Exchange, QMFService.Listener return getMsgReceives(); } + public long getMsgDrops() + { + return 0l; + } + public long getByteReceives() { return _bytesReceived.get(); @@ -550,6 +555,11 @@ public class ManagementExchange implements Exchange, QMFService.Listener return getByteReceives(); } + public long getByteDrops() + { + return 0l; + } + public long getCreateTime() { return _createTime; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFMessage.java b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFMessage.java index 787cede2b7..1b173c7e11 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFMessage.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFMessage.java @@ -21,6 +21,8 @@ package org.apache.qpid.qmf; +import java.util.Collection; +import java.util.Collections; import org.apache.commons.lang.NotImplementedException; import org.apache.qpid.framing.AMQShortString; @@ -111,6 +113,16 @@ public class QMFMessage implements ServerMessage, InboundMessage, AMQMessageHead return 0; } + public String getUserId() + { + return null; + } + + public String getAppId() + { + return null; + } + public String getMessageId() { return null; @@ -166,6 +178,12 @@ public class QMFMessage implements ServerMessage, InboundMessage, AMQMessageHead return false; } + @Override + public Collection getHeaderNames() + { + return Collections.EMPTY_SET; + } + public boolean containsHeader(String name) { return false; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java deleted file mode 100644 index 0f32b98aa8..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ /dev/null @@ -1,410 +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; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.management.common.mbeans.ManagedBroker; -import org.apache.qpid.management.common.mbeans.ManagedQueue; -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.exchange.ExchangeFactory; -import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.exchange.ExchangeType; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.model.UUIDGenerator; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.AMQQueueFactory; -import org.apache.qpid.server.queue.AMQQueueMBean; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.virtualhost.VirtualHostImpl; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * This MBean implements the broker management interface and exposes the - * Broker level management features like creating and deleting exchanges and queue. - */ -@MBeanDescription("This MBean exposes the broker level management features") -public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBroker -{ - private final QueueRegistry _queueRegistry; - private final ExchangeRegistry _exchangeRegistry; - private final ExchangeFactory _exchangeFactory; - - private final VirtualHostImpl.VirtualHostMBean _virtualHostMBean; - - @MBeanConstructor("Creates the Broker Manager MBean") - public AMQBrokerManagerMBean(VirtualHostImpl.VirtualHostMBean virtualHostMBean) throws JMException - { - super(ManagedBroker.class, ManagedBroker.TYPE); - - _virtualHostMBean = virtualHostMBean; - VirtualHost virtualHost = virtualHostMBean.getVirtualHost(); - - _queueRegistry = virtualHost.getQueueRegistry(); - _exchangeRegistry = virtualHost.getExchangeRegistry(); - _exchangeFactory = virtualHost.getExchangeFactory(); - } - - public String getObjectInstanceName() - { - return _virtualHostMBean.getVirtualHost().getName(); - } - - /** - * Returns an array of the exchange types available for creation. - * @since Qpid JMX API 1.3 - * @throws IOException - */ - public String[] getExchangeTypes() throws IOException - { - ArrayList exchangeTypes = new ArrayList(); - for(ExchangeType ex : _exchangeFactory.getPublicCreatableTypes()) - { - exchangeTypes.add(ex.getName().toString()); - } - - return exchangeTypes.toArray(new String[0]); - } - - /** - * Returns a list containing the names of the attributes available for the Queue mbeans. - * @since Qpid JMX API 1.3 - * @throws IOException - */ - public List retrieveQueueAttributeNames() throws IOException - { - return ManagedQueue.QUEUE_ATTRIBUTES; - } - - /** - * Returns a List of Object Lists containing the requested attribute values (in the same sequence requested) for each queue in the virtualhost. - * If a particular attribute cant be found or raises an mbean/reflection exception whilst being gathered its value is substituted with the String "-". - * @since Qpid JMX API 1.3 - * @throws IOException - */ - public List> retrieveQueueAttributeValues(String[] attributes) throws IOException - { - if(_queueRegistry.getQueues().size() == 0) - { - return new ArrayList>(); - } - - List> queueAttributesList = new ArrayList>(_queueRegistry.getQueues().size()); - - int attributesLength = attributes.length; - - for(AMQQueue queue : _queueRegistry.getQueues()) - { - AMQQueueMBean mbean = (AMQQueueMBean) queue.getManagedObject(); - - if(mbean == null) - { - continue; - } - - List attributeValues = new ArrayList(attributesLength); - - for(int i=0; i < attributesLength; i++) - { - try - { - attributeValues.add(mbean.getAttribute(attributes[i])); - } - catch (Exception e) - { - attributeValues.add("-"); - } - } - - queueAttributesList.add(attributeValues); - } - - return queueAttributesList; - } - - /** - * Creates new exchange and registers it with the registry. - * - * @param exchangeName - * @param type - * @param durable - * @throws JMException - * @throws MBeanException - */ - public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException, MBeanException - { - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - synchronized (_exchangeRegistry) - { - Exchange exchange = _exchangeRegistry.getExchange(new AMQShortString(exchangeName)); - if (exchange == null) - { - exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), - new AMQShortString(type), durable, false, 0); - _exchangeRegistry.registerExchange(exchange); - if (durable) - { - getVirtualHost().getMessageStore().createExchange(exchange); - } - } - else - { - throw new JMException("The exchange \"" + exchangeName + "\" already exists."); - } - } - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in creating exchange " + exchangeName); - } - finally - { - CurrentActor.remove(); - } - } - - /** - * Unregisters the exchange from registry. - * - * @param exchangeName - * @throws JMException - * @throws MBeanException - */ - public void unregisterExchange(String exchangeName) throws JMException, MBeanException - { - // TODO - // Check if the exchange is in use. - - // Check if there are queue-bindings with the exchange and unregister - // when there are no bindings. - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - _exchangeRegistry.unregisterExchange(new AMQShortString(exchangeName), false); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in unregistering exchange " + exchangeName); - } - finally - { - CurrentActor.remove(); - } - } - - /** - * Creates a new queue and registers it with the registry and puts it - * in persistance storage if durable queue. - * - * @param queueName - * @param durable - * @param owner - * @throws JMException - * @throws MBeanException - */ - public void createNewQueue(String queueName, String owner, boolean durable) throws JMException, MBeanException - { - createNewQueue(queueName, owner, durable, null); - } - - public void createNewQueue(String queueName, String owner, boolean durable, Map arguments) throws JMException - { - final AMQShortString queueNameAsAMQShortString = new AMQShortString(queueName); - synchronized (_queueRegistry) - { - AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); - if (queue != null) - { - throw new JMException("The queue \"" + queueName + "\" already exists."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - FieldTable args = null; - if(arguments != null) - { - args = FieldTable.convertToFieldTable(arguments); - } - final VirtualHost virtualHost = getVirtualHost(); - - queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), queueName, durable, owner, - false, false, getVirtualHost(), arguments); - if (queue.isDurable() && !queue.isAutoDelete()) - { - getVirtualHost().getMessageStore().createQueue(queue, args); - } - - virtualHost.getBindingFactory().addBinding(queueName, queue, _exchangeRegistry.getDefaultExchange(), null); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in creating queue " + queueName); - } - finally - { - CurrentActor.remove(); - } - } - } - - private VirtualHost getVirtualHost() - { - return _virtualHostMBean.getVirtualHost(); - } - - /** - * Deletes the queue from queue registry and persistant storage. - * - * @param queueName - * @throws JMException - * @throws MBeanException - */ - public void deleteQueue(String queueName) throws JMException, MBeanException - { - AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("The Queue " + queueName + " is not a registered queue."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - queue.delete(); - if (queue.isDurable()) - { - getVirtualHost().getMessageStore().removeQueue(queue); - } - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in deleting queue " + queueName); - } - finally - { - CurrentActor.remove(); - } - } - - @Override - public ManagedObject getParentObject() - { - return _virtualHostMBean; - } - - // This will have a single instance for a virtual host, so not having the name property in the ObjectName - @Override - public ObjectName getObjectName() throws MalformedObjectNameException - { - return getObjectNameForSingleInstanceMBean(); - } - - public void resetStatistics() throws Exception - { - getVirtualHost().resetStatistics(); - } - - public double getPeakMessageDeliveryRate() - { - return getVirtualHost().getMessageDeliveryStatistics().getPeak(); - } - - public double getPeakDataDeliveryRate() - { - return getVirtualHost().getDataDeliveryStatistics().getPeak(); - } - - public double getMessageDeliveryRate() - { - return getVirtualHost().getMessageDeliveryStatistics().getRate(); - } - - public double getDataDeliveryRate() - { - return getVirtualHost().getDataDeliveryStatistics().getRate(); - } - - public long getTotalMessagesDelivered() - { - return getVirtualHost().getMessageDeliveryStatistics().getTotal(); - } - - public long getTotalDataDelivered() - { - return getVirtualHost().getDataDeliveryStatistics().getTotal(); - } - - public double getPeakMessageReceiptRate() - { - return getVirtualHost().getMessageReceiptStatistics().getPeak(); - } - - public double getPeakDataReceiptRate() - { - return getVirtualHost().getDataReceiptStatistics().getPeak(); - } - - public double getMessageReceiptRate() - { - return getVirtualHost().getMessageReceiptStatistics().getRate(); - } - - public double getDataReceiptRate() - { - return getVirtualHost().getDataReceiptStatistics().getRate(); - } - - public long getTotalMessagesReceived() - { - return getVirtualHost().getMessageReceiptStatistics().getTotal(); - } - - public long getTotalDataReceived() - { - return getVirtualHost().getDataReceiptStatistics().getTotal(); - } - - public boolean isStatisticsEnabled() - { - return getVirtualHost().isStatisticsEnabled(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 4fd4e02220..030fd3f499 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -140,7 +140,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm private UnacknowledgedMessageMap _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(DEFAULT_PREFETCH); - // Set of messages being acknoweledged in the current transaction + // Set of messages being acknowledged in the current transaction private SortedSet _acknowledgedMessages = new TreeSet(); private final AtomicBoolean _suspended = new AtomicBoolean(false); @@ -263,6 +263,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm return _txnCount.get(); } + public Long getTxnStart() + { + return _txnStarts.get(); + } + public int getChannelId() { return _channelId; @@ -440,7 +445,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm * @param acks Are acks enabled for this subscriber * @param filters Filters to apply to this subscriber * - * @param noLocal Flag stopping own messages being receivied. + * @param noLocal Flag stopping own messages being received. * @param exclusive Flag requesting exclusive access to the queue * @return the consumer tag. This is returned to the subscriber and used in subsequent unsubscribe requests * @@ -1417,6 +1422,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm return false; } + public int getUnacknowledgedMessageCount() + { + return getUnacknowledgedMessageMap().size(); + } + private void flow(boolean flow) { MethodRegistry methodRegistry = _session.getMethodRegistry(); @@ -1424,6 +1434,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm _session.writeFrame(responseBody.generateFrame(_channelId)); } + @Override public boolean getBlocking() { return _blocking.get(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java index 90603777d1..c843ce6a23 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -25,24 +25,17 @@ import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; +import java.util.*; import javax.net.ssl.SSLContext; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.QpidLog4JConfigurator; import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.ServerNetworkTransportConfiguration; -import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; -import org.apache.qpid.server.information.management.ServerInformationMBean; import org.apache.qpid.server.logging.SystemOutMessageLogger; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.server.logging.management.LoggingManagementMBean; import org.apache.qpid.server.logging.messages.BrokerMessages; import org.apache.qpid.server.protocol.AmqpProtocolVersion; import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory; @@ -118,6 +111,14 @@ public class Broker ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile, options.getBundleContext()); ServerConfiguration serverConfig = config.getConfiguration(); + if (options.getQpidWork() != null) + { + serverConfig.setQpidWork(options.getQpidWork()); + } + if (options.getQpidHome() != null) + { + serverConfig.setQpidHome(options.getQpidHome()); + } updateManagementPorts(serverConfig, options.getJmxPortRegistryServer(), options.getJmxPortConnectorServer()); ApplicationRegistry.initialise(config); @@ -135,14 +136,6 @@ public class Broker try { - configureLoggingManagementMBean(logConfigFile, options.getLogWatchFrequency()); - - ConfigurationManagementMBean configMBean = new ConfigurationManagementMBean(); - configMBean.register(); - - ServerInformationMBean sysInfoMBean = new ServerInformationMBean(config); - sysInfoMBean.register(); - Set ports = new HashSet(options.getPorts()); if(ports.isEmpty()) { @@ -258,7 +251,7 @@ public class Broker transport.accept(settings, protocolEngineFactory, null); ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, - new QpidAcceptor(transport,"TCP")); + new QpidAcceptor(transport,QpidAcceptor.Transport.TCP, supported)); CurrentActor.get().message(BrokerMessages.LISTENING("TCP", port)); } } @@ -302,7 +295,7 @@ public class Broker transport.accept(settings, protocolEngineFactory, sslContext); ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, - new QpidAcceptor(transport,"TCP")); + new QpidAcceptor(transport,QpidAcceptor.Transport.SSL, supported)); CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", sslPort)); } } @@ -495,12 +488,6 @@ public class Broker } } - private void configureLoggingManagementMBean(File logConfigFile, int logWatchTime) throws Exception - { - LoggingManagementMBean blm = new LoggingManagementMBean(logConfigFile.getPath(),logWatchTime); - - blm.register(); - } private void addShutdownHook() { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java index cec614881d..434d40d557 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java @@ -33,6 +33,7 @@ public class BrokerOptions public static final String DEFAULT_CONFIG_FILE = "etc/config.xml"; public static final String DEFAULT_LOG_CONFIG_FILE = "etc/log4j.xml"; public static final String QPID_HOME = "QPID_HOME"; + public static final String QPID_WORK = "QPID_WORK"; private final Set _ports = new HashSet(); private final Set _sslPorts = new HashSet(); @@ -47,6 +48,8 @@ public class BrokerOptions private BundleContext _bundleContext; private Integer _logWatchFrequency = 0; + private String _qpidWorkFolder; + private String _qpidHomeFolder; public void addPort(final int port) { @@ -109,7 +112,7 @@ public class BrokerOptions } public String getQpidHome() { - return System.getProperty(QPID_HOME); + return _qpidHomeFolder == null? System.getProperty(QPID_HOME): _qpidHomeFolder; } public Set getExcludedPorts(final ProtocolExclusion excludeProtocol) @@ -179,4 +182,19 @@ public class BrokerOptions Set ports = _inclusionMap.get(includeProtocol); ports.add(port); } + + public String getQpidWork() + { + return _qpidWorkFolder; + } + + public void setQpidWork(String qpidWorkFolder) + { + _qpidWorkFolder = qpidWorkFolder; + } + + public void setQpidHome(String qpidHomeFolder) + { + _qpidHomeFolder = qpidHomeFolder; + } } \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ExchangeConfig.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ExchangeConfig.java index 41c51d9684..6633d93adf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ExchangeConfig.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ExchangeConfig.java @@ -49,7 +49,12 @@ public interface ExchangeConfig extends ConfiguredObject envVarMap = new HashMap(); @@ -110,6 +113,8 @@ public class ServerConfiguration extends ConfigurationPlugin envVarMap.put("QPID_MSGAUTH", "security.msg-auth"); envVarMap.put("QPID_AUTOREGISTER", "auto_register"); envVarMap.put("QPID_MANAGEMENTENABLED", "management.enabled"); + envVarMap.put("QPID_HTTPMANAGEMENTENABLED", "management.http.enabled"); + envVarMap.put("QPID_HTTPMANAGEMENTPORT", "management.http.port"); envVarMap.put("QPID_HEARTBEATDELAY", "heartbeat.delay"); envVarMap.put("QPID_HEARTBEATTIMEOUTFACTOR", "heartbeat.timeoutFactor"); envVarMap.put("QPID_MAXIMUMMESSAGEAGE", "maximumMessageAge"); @@ -511,12 +516,26 @@ public class ServerConfiguration extends ConfigurationPlugin public String getQpidWork() { - return System.getProperty(QPID_WORK, System.getProperty("java.io.tmpdir")); + if ( _qpidWork == null ) + { + return System.getProperty(QPID_WORK, System.getProperty("java.io.tmpdir")); + } + else + { + return _qpidWork; + } } public String getQpidHome() { - return System.getProperty(QPID_HOME); + if ( _qpidHome == null ) + { + return System.getProperty(QPID_HOME); + } + else + { + return _qpidHome; + } } public void setJMXPortRegistryServer(int registryServerPort) @@ -554,6 +573,16 @@ public class ServerConfiguration extends ConfigurationPlugin return getBooleanValue("management.platform-mbeanserver", true); } + public boolean getHTTPManagementEnabled() + { + return getBooleanValue("management.http.enabled", true); + } + + public int getHTTPManagementPort() + { + return getIntValue("management.http.port", 8080); + } + public String[] getVirtualHosts() { return _virtualHosts.keySet().toArray(new String[_virtualHosts.size()]); @@ -622,7 +651,7 @@ public class ServerConfiguration extends ConfigurationPlugin public boolean getManagementSSLEnabled() { - return getBooleanValue("management.ssl.enabled", true); + return getBooleanValue("management.ssl.enabled", false); } public String getManagementKeyStorePassword() @@ -636,16 +665,11 @@ public class ServerConfiguration extends ConfigurationPlugin return getBooleanValue("queue.auto_register", true); } - public boolean getManagementEnabled() + public boolean getJMXManagementEnabled() { return getBooleanValue("management.enabled", true); } - public void setManagementEnabled(boolean enabled) - { - getConfig().setProperty("management.enabled", enabled); - } - public int getHeartBeatDelay() { return getIntValue("heartbeat.delay", 5); @@ -981,4 +1005,14 @@ public class ServerConfiguration extends ConfigurationPlugin return reply == null ? null : AmqpProtocolVersion.valueOf(reply); } + + public void setQpidWork(String path) + { + _qpidWork = path; + } + + public void setQpidHome(String path) + { + _qpidHome = path; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java deleted file mode 100644 index f0ca5dc139..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/management/ConfigurationManagementMBean.java +++ /dev/null @@ -1,47 +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.configuration.management; - -import org.apache.qpid.management.common.mbeans.ConfigurationManagement; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.registry.ApplicationRegistry; - -import javax.management.NotCompliantMBeanException; - -public class ConfigurationManagementMBean extends AMQManagedObject implements ConfigurationManagement -{ - - public ConfigurationManagementMBean() throws NotCompliantMBeanException - { - super(ConfigurationManagement.class, ConfigurationManagement.TYPE); - } - - public String getObjectInstanceName() - { - return ConfigurationManagement.TYPE; - } - - public void reloadSecurityConfiguration() throws Exception - { - ApplicationRegistry.getInstance().getConfiguration().reparseConfigFileSecuritySections(); - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index a539743081..451754e6d8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -88,6 +88,13 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable } } } + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.connectionRegistered(connnection); + } + } } public void deregisterConnection(AMQConnectionModel connnection) @@ -104,6 +111,14 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable } } } + + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.connectionUnregistered(connnection); + } + } } public void addRegistryChangeListener(RegistryChangeListener listener) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index 9493f400f2..e633ddd341 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -30,15 +30,12 @@ import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.ExchangeLogSubject; -import org.apache.qpid.server.management.Managable; -import org.apache.qpid.server.management.ManagedObject; 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.queue.QueueRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; -import javax.management.JMException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -50,10 +47,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -public abstract class AbstractExchange implements Exchange, Managable +public abstract class AbstractExchange implements Exchange { - - private AMQShortString _name; private final AtomicBoolean _closed = new AtomicBoolean(); @@ -66,9 +61,6 @@ public abstract class AbstractExchange implements Exchange, Managable private final List _closeTaskList = new CopyOnWriteArrayList(); - - private AbstractExchangeMBean _exchangeMbean; - /** * Whether the exchange is automatically deleted once all queues have detached from it */ @@ -86,6 +78,8 @@ public abstract class AbstractExchange implements Exchange, Managable private final AtomicLong _receivedMessageSize = new AtomicLong(); private final AtomicLong _routedMessageCount = new AtomicLong(); private final AtomicLong _routedMessageSize = new AtomicLong(); + private final AtomicLong _droppedMessageCount = new AtomicLong(); + private final AtomicLong _droppedMessageSize = new AtomicLong(); private final CopyOnWriteArrayList _listeners = new CopyOnWriteArrayList(); @@ -107,13 +101,6 @@ public abstract class AbstractExchange implements Exchange, Managable return _type.getName(); } - /** - * Concrete exchanges must implement this method in order to create the managed representation. This is - * called during initialisation (template method pattern). - * @return the MBean - */ - protected abstract AbstractExchangeMBean createMBean() throws JMException; - public void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { @@ -126,26 +113,12 @@ public abstract class AbstractExchange implements Exchange, Managable _id = id; getConfigStore().addConfiguredObject(this); - createAndRegisterMBean(); _logSubject = new ExchangeLogSubject(this, this.getVirtualHost()); // Log Exchange creation CurrentActor.get().message(ExchangeMessages.CREATED(String.valueOf(getTypeShortString()), String.valueOf(name), durable)); } - private void createAndRegisterMBean() - { - try - { - _exchangeMbean = createMBean(); - _exchangeMbean.register(); - } - catch (JMException e) - { - throw new RuntimeException("Failed to register mbean",e); - } - } - public ConfigStore getConfigStore() { return getVirtualHost().getConfigStore(); @@ -171,10 +144,6 @@ public abstract class AbstractExchange implements Exchange, Managable if(_closed.compareAndSet(false,true)) { - if (_exchangeMbean != null) - { - _exchangeMbean.unregister(); - } getConfigStore().removeConfiguredObject(this); if(_alternateExchange != null) { @@ -196,11 +165,6 @@ public abstract class AbstractExchange implements Exchange, Managable return getClass().getSimpleName() + "[" + getNameShortString() +"]"; } - public ManagedObject getManagedObject() - { - return _exchangeMbean; - } - public VirtualHost getVirtualHost() { return _virtualHost; @@ -359,6 +323,11 @@ public abstract class AbstractExchange implements Exchange, Managable _routedMessageCount.incrementAndGet(); _routedMessageSize.addAndGet(message.getSize()); } + else + { + _droppedMessageCount.incrementAndGet(); + _droppedMessageSize.addAndGet(message.getSize()); + } return queues; } @@ -374,6 +343,11 @@ public abstract class AbstractExchange implements Exchange, Managable return _routedMessageCount.get(); } + public long getMsgDrops() + { + return _droppedMessageCount.get(); + } + public long getByteReceives() { return _receivedMessageSize.get(); @@ -384,6 +358,11 @@ public abstract class AbstractExchange implements Exchange, Managable return _routedMessageSize.get(); } + public long getByteDrops() + { + return _droppedMessageSize.get(); + } + public long getCreateTime() { return _createTime; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java deleted file mode 100644 index 034331abd9..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java +++ /dev/null @@ -1,215 +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.exchange; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.management.common.mbeans.ManagedExchange; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.management.ManagedObjectRegistry; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.MalformedObjectNameException; -import javax.management.NotCompliantMBeanException; -import javax.management.ObjectName; -import javax.management.openmbean.ArrayType; -import javax.management.openmbean.CompositeType; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.SimpleType; -import javax.management.openmbean.TabularType; -import java.util.Collections; - - -/** - * Abstract MBean class. This has some of the methods implemented from - * management intrerface for exchanges. Any implementaion of an - * Exchange MBean should extend this class. - */ -public abstract class AbstractExchangeMBean extends AMQManagedObject implements ManagedExchange -{ - // open mbean data types for representing exchange bindings - private OpenType[] _bindingItemTypes; - private CompositeType _bindingDataType; - private TabularType _bindinglistDataType; - - - private T _exchange; - - public AbstractExchangeMBean(final T abstractExchange) throws NotCompliantMBeanException - { - super(ManagedExchange.class, ManagedExchange.TYPE); - _exchange = abstractExchange; - } - - protected void init() throws OpenDataException - { - _bindingItemTypes = new OpenType[2]; - _bindingItemTypes[0] = SimpleType.STRING; - _bindingItemTypes[1] = new ArrayType(1, SimpleType.STRING); - _bindingDataType = new CompositeType("Exchange Binding", "Binding key and Queue names", - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), - COMPOSITE_ITEM_DESCRIPTIONS.toArray(new String[COMPOSITE_ITEM_DESCRIPTIONS.size()]), _bindingItemTypes); - _bindinglistDataType = new TabularType("Exchange Bindings", "Exchange Bindings for " + getName(), - _bindingDataType, TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); - } - - public ManagedObject getParentObject() - { - return _exchange.getVirtualHost().getManagedObject(); - } - - public T getExchange() - { - return _exchange; - } - - - public String getObjectInstanceName() - { - return ObjectName.quote(_exchange.getName()); - } - - public String getName() - { - return _exchange.getName(); - } - - public String getExchangeType() - { - return _exchange.getTypeShortString().toString(); - } - - public Integer getTicketNo() - { - return _exchange.getTicket(); - } - - public boolean isDurable() - { - return _exchange.isDurable(); - } - - public boolean isAutoDelete() - { - return _exchange.isAutoDelete(); - } - - // Added exchangetype in the object name lets maangement apps to do any customization required - public ObjectName getObjectName() throws MalformedObjectNameException - { - String objNameString = super.getObjectName().toString(); - objNameString = objNameString + ",ExchangeType=" + getExchangeType(); - return new ObjectName(objNameString); - } - - protected ManagedObjectRegistry getManagedObjectRegistry() - { - return ApplicationRegistry.getInstance().getManagedObjectRegistry(); - } - - public void createNewBinding(String queueName, String binding) throws JMException - { - VirtualHost vhost = getExchange().getVirtualHost(); - AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("Queue \"" + queueName + "\" is not registered with the virtualhost."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - vhost.getBindingFactory().addBinding(binding,queue,getExchange(),null); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error creating new binding " + binding); - } - CurrentActor.remove(); - } - - /** - * Removes a queue binding from the exchange. - * - * @see org.apache.qpid.server.binding.BindingFactory#removeBinding(String, AMQQueue, Exchange, java.util.Map) - */ - public void removeBinding(String queueName, String binding) throws JMException - { - VirtualHost vhost = getExchange().getVirtualHost(); - AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("Queue \"" + queueName + "\" is not registered with the virtualhost."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - vhost.getBindingFactory().removeBinding(binding, queue, _exchange, Collections.emptyMap()); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error removing binding " + binding); - } - CurrentActor.remove(); - } - - - protected OpenType[] getBindingItemTypes() - { - return _bindingItemTypes; - } - - protected void setBindingItemTypes(OpenType[] bindingItemTypes) - { - _bindingItemTypes = bindingItemTypes; - } - - protected CompositeType getBindingDataType() - { - return _bindingDataType; - } - - protected void setBindingDataType(CompositeType bindingDataType) - { - _bindingDataType = bindingDataType; - } - - protected TabularType getBindinglistDataType() - { - return _bindinglistDataType; - } - - protected void setBindinglistDataType(TabularType bindinglistDataType) - { - _bindinglistDataType = bindinglistDataType; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index bf4184bf0b..baf9cc3d09 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -29,7 +29,9 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -46,6 +48,8 @@ public class DefaultExchangeRegistry implements ExchangeRegistry private Exchange _defaultExchange; private VirtualHost _host; + private final Collection _listeners = + Collections.synchronizedCollection(new ArrayList()); public DefaultExchangeRegistry(VirtualHost host) { @@ -68,6 +72,14 @@ public class DefaultExchangeRegistry implements ExchangeRegistry { _exchangeMap.put(exchange.getNameShortString(), exchange); _exchangeMapStr.put(exchange.getNameShortString().toString(), exchange); + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.exchangeRegistered(exchange); + } + + } } public void setDefaultExchange(Exchange exchange) @@ -114,6 +126,15 @@ public class DefaultExchangeRegistry implements ExchangeRegistry getDurableConfigurationStore().removeExchange(e); } e.close(); + + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.exchangeUnregistered(exchange); + } + } + } else { @@ -126,6 +147,16 @@ public class DefaultExchangeRegistry implements ExchangeRegistry unregisterExchange(new AMQShortString(name), inUse); } + public Collection getExchanges() + { + return new ArrayList(_exchangeMap.values()); + } + + public void addRegistryChangeListener(RegistryChangeListener listener) + { + _listeners.add(listener); + } + public Exchange getExchange(AMQShortString name) { if ((name == null) || name.length() == 0) @@ -158,16 +189,14 @@ public class DefaultExchangeRegistry implements ExchangeRegistry { final Exchange exchange = getExchange(exchangeName); - if (exchange instanceof AbstractExchange) + //TODO: this is a bit of a hack, what if the listeners aren't aware + //that we are just unregistering the MBean because of HA, and aren't + //actually removing the exchange as such. + synchronized (_listeners) { - AbstractExchange abstractExchange = (AbstractExchange) exchange; - try - { - abstractExchange.getManagedObject().unregister(); - } - catch (AMQException e) + for(RegistryChangeListener listener : _listeners) { - LOGGER.warn("Failed to unregister mbean", e); + listener.exchangeUnregistered(exchange); } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java index af9322764a..92326412c1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java @@ -127,11 +127,6 @@ public class DirectExchange extends AbstractExchange super(TYPE); } - protected AbstractExchangeMBean createMBean() throws JMException - { - return new DirectExchangeMBean(this); - } - public List doRoute(InboundMessage payload) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchangeMBean.java deleted file mode 100644 index 0bfaf7035d..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchangeMBean.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.exchange; - -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 javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * MBean class implementing the management interfaces. - */ -@MBeanDescription("Management Bean for Direct Exchange") -final class DirectExchangeMBean extends AbstractExchangeMBean -{ - @MBeanConstructor("Creates an MBean for AMQ direct exchange") - public DirectExchangeMBean(final DirectExchange exchange) throws JMException - { - super(exchange); - - init(); - } - - public TabularData bindings() throws OpenDataException - { - TabularDataSupport bindingList = new TabularDataSupport(getBindinglistDataType()); - - Map> bindingMap = new HashMap>(); - - for (Binding binding : getExchange().getBindings()) - { - String key = binding.getBindingKey(); - List queueList = bindingMap.get(key); - if(queueList == null) - { - queueList = new ArrayList(); - bindingMap.put(key, queueList); - } - queueList.add(binding.getQueue().getNameShortString().toString()); - - } - - for(Map.Entry> entry : bindingMap.entrySet()) - { - Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[0])}; - CompositeData bindingData = new CompositeDataSupport(getBindingDataType(), - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), - bindingItemValues); - bindingList.put(bindingData); - } - - return bindingList; - } - - - -}// End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java index 795ae2e140..692a2b2b0d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.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 @@ -57,4 +57,14 @@ public interface ExchangeRegistry void clearAndUnregisterMbeans(); Exchange getExchange(UUID exchangeId); + + Collection getExchanges(); + + void addRegistryChangeListener(RegistryChangeListener listener); + + interface RegistryChangeListener + { + void exchangeRegistered(Exchange exchange); + void exchangeUnregistered(Exchange exchange); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index 5ebcfd095f..5f4998f77f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -48,11 +48,6 @@ public class FanoutExchange extends AbstractExchange */ private final ConcurrentHashMap _queues = new ConcurrentHashMap(); - protected AbstractExchangeMBean createMBean() throws JMException - { - return new FanoutExchangeMBean(this); - } - public static final ExchangeType TYPE = new ExchangeType() { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchangeMBean.java deleted file mode 100644 index 61e23c896c..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchangeMBean.java +++ /dev/null @@ -1,74 +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.exchange; - -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 javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.ArrayList; - -/** - * MBean class implementing the management interfaces. - */ -@MBeanDescription("Management Bean for Fanout Exchange") -final class FanoutExchangeMBean extends AbstractExchangeMBean -{ - private static final String BINDING_KEY_SUBSTITUTE = "*"; - - @MBeanConstructor("Creates an MBean for AMQ fanout exchange") - public FanoutExchangeMBean(final FanoutExchange exchange) throws JMException - { - super(exchange); - init(); - } - - public TabularData bindings() throws OpenDataException - { - - TabularDataSupport bindingList = new TabularDataSupport(getBindinglistDataType()); - - - ArrayList queueNames = new ArrayList(); - - for (Binding binding : getExchange().getBindings()) - { - String queueName = binding.getQueue().getNameShortString().toString(); - queueNames.add(queueName); - } - - Object[] bindingItemValues = {BINDING_KEY_SUBSTITUTE, queueNames.toArray(new String[0])}; - CompositeData bindingData = new CompositeDataSupport(getBindingDataType(), - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), - bindingItemValues); - bindingList.put(bindingData); - - return bindingList; - } - - -} // End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index 16ba3c0431..6bad59c2ae 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -227,11 +227,6 @@ public class HeadersExchange extends AbstractExchange return !getBindings().isEmpty(); } - protected AbstractExchangeMBean createMBean() throws JMException - { - return new HeadersExchangeMBean(this); - } - protected void onBind(final Binding binding) { String bindingKey = binding.getBindingKey(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java deleted file mode 100644 index 395c6c8a91..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java +++ /dev/null @@ -1,158 +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.exchange; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -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.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.openmbean.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.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * HeadersExchangeMBean class implements the management interface for the - * Header Exchanges. - */ -@MBeanDescription("Management Bean for Headers Exchange") -final class HeadersExchangeMBean extends AbstractExchangeMBean -{ - - @MBeanConstructor("Creates an MBean for AMQ Headers exchange") - public HeadersExchangeMBean(final HeadersExchange headersExchange) throws JMException - { - super(headersExchange); - init(); - } - - /** - * initialises the OpenType objects. - */ - protected void init() throws OpenDataException - { - - setBindingItemTypes(new OpenType[3]); - getBindingItemTypes()[0] = SimpleType.INTEGER; - getBindingItemTypes()[1] = SimpleType.STRING; - getBindingItemTypes()[2] = new ArrayType(1, SimpleType.STRING); - setBindingDataType(new CompositeType("Exchange Binding", "Queue name and header bindings", - HEADERS_COMPOSITE_ITEM_NAMES.toArray(new String[HEADERS_COMPOSITE_ITEM_NAMES.size()]), - HEADERS_COMPOSITE_ITEM_DESC.toArray(new String[HEADERS_COMPOSITE_ITEM_DESC.size()]), getBindingItemTypes())); - setBindinglistDataType(new TabularType("Exchange Bindings", "List of exchange bindings for " + getName(), - getBindingDataType(), HEADERS_TABULAR_UNIQUE_INDEX.toArray(new String[HEADERS_TABULAR_UNIQUE_INDEX.size()]))); - } - - public TabularData bindings() throws OpenDataException - { - TabularDataSupport bindingList = new TabularDataSupport(getBindinglistDataType()); - int count = 1; - for (Binding binding : getExchange().getBindings()) - { - - String queueName = binding.getQueue().getNameShortString().toString(); - - - Map headerMappings = binding.getArguments(); - final List mappingList = new ArrayList(); - - if(headerMappings != null) - { - for(Map.Entry entry : headerMappings.entrySet()) - { - - mappingList.add(entry.getKey() + "=" + entry.getValue()); - } - } - - - Object[] bindingItemValues = {count++, queueName, mappingList.toArray(new String[0])}; - CompositeData bindingData = new CompositeDataSupport(getBindingDataType(), - HEADERS_COMPOSITE_ITEM_NAMES.toArray(new String[HEADERS_COMPOSITE_ITEM_NAMES.size()]), bindingItemValues); - bindingList.put(bindingData); - } - - return bindingList; - } - - @Override - public void createNewBinding(String queueName, String binding) throws JMException - { - VirtualHost vhost = getExchange().getVirtualHost(); - AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("Queue \"" + queueName + "\" is not registered with the virtualhost."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - - final Map arguments = new HashMap(); - 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 \"=,=\" "); - } - - if(keyAndValue.length == 1) - { - //no value was given, only a key. Use an empty value to signal match on key presence alone - arguments.put(keyAndValue[0], ""); - } - else - { - arguments.put(keyAndValue[0], keyAndValue[1]); - } - } - try - { - vhost.getBindingFactory().addBinding(binding,queue,getExchange(),arguments); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error creating new binding " + binding); - } - CurrentActor.remove(); - } - -} // End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java index 7ea7a41826..480d4e4215 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -403,11 +403,6 @@ public class TopicExchange extends AbstractExchange } } - protected AbstractExchangeMBean createMBean() throws JMException - { - return new TopicExchangeMBean(this); - } - private Collection getMatchedQueues(InboundMessage message, AMQShortString routingKey) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchangeMBean.java deleted file mode 100644 index 481a377fc4..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchangeMBean.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.exchange; - -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 javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** TopicExchangeMBean class implements the management interface for the Topic exchanges. */ -@MBeanDescription("Management Bean for Topic Exchange") -final class TopicExchangeMBean extends AbstractExchangeMBean -{ - private TopicExchange _topicExchange; - - @MBeanConstructor("Creates an MBean for AMQ topic exchange") - public TopicExchangeMBean(final TopicExchange topicExchange) throws JMException - { - super(topicExchange); - init(); - } - - /** returns exchange bindings in tabular form */ - public TabularData bindings() throws OpenDataException - { - TabularDataSupport bindingList = new TabularDataSupport(getBindinglistDataType()); - Map> bindingData = new HashMap>(); - for (Binding binding : getExchange().getBindings()) - { - String key = binding.getBindingKey(); - List queueNames = bindingData.get(key); - if(queueNames == null) - { - queueNames = new ArrayList(); - bindingData.put(key, queueNames); - } - queueNames.add(binding.getQueue().getNameShortString().toString()); - - } - for(Map.Entry> entry : bindingData.entrySet()) - { - Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]) }; - CompositeData bindingCompositeData = - new CompositeDataSupport(getBindingDataType(), - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), - bindingItemValues); - bindingList.put(bindingCompositeData); - } - - return bindingList; - } - -} // End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java deleted file mode 100644 index 4d395f625a..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java +++ /dev/null @@ -1,145 +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.information.management; - -import org.apache.qpid.common.QpidProperties; -import org.apache.qpid.management.common.mbeans.ServerInformation; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.registry.ApplicationRegistry; - -import javax.management.JMException; -import java.io.IOException; - -/** MBean class for the ServerInformationMBean. */ -@MBeanDescription("Server Information Interface") -public class ServerInformationMBean extends AMQManagedObject implements ServerInformation -{ - private String buildVersion; - private String productVersion; - private ApplicationRegistry registry; - - public ServerInformationMBean(ApplicationRegistry applicationRegistry) throws JMException - { - super(ServerInformation.class, ServerInformation.TYPE); - - registry = applicationRegistry; - buildVersion = QpidProperties.getBuildVersion(); - productVersion = QpidProperties.getReleaseVersion(); - } - - public String getObjectInstanceName() - { - return ServerInformation.TYPE; - } - - public Integer getManagementApiMajorVersion() throws IOException - { - return QPID_JMX_API_MAJOR_VERSION; - } - - public Integer getManagementApiMinorVersion() throws IOException - { - return QPID_JMX_API_MINOR_VERSION; - } - - public String getBuildVersion() throws IOException - { - return buildVersion; - } - - public String getProductVersion() throws IOException - { - return productVersion; - } - - - public void resetStatistics() throws Exception - { - registry.resetStatistics(); - } - - public double getPeakMessageDeliveryRate() - { - return registry.getMessageDeliveryStatistics().getPeak(); - } - - public double getPeakDataDeliveryRate() - { - return registry.getDataDeliveryStatistics().getPeak(); - } - - public double getMessageDeliveryRate() - { - return registry.getMessageDeliveryStatistics().getRate(); - } - - public double getDataDeliveryRate() - { - return registry.getDataDeliveryStatistics().getRate(); - } - - public long getTotalMessagesDelivered() - { - return registry.getMessageDeliveryStatistics().getTotal(); - } - - public long getTotalDataDelivered() - { - return registry.getDataDeliveryStatistics().getTotal(); - } - - public double getPeakMessageReceiptRate() - { - return registry.getMessageReceiptStatistics().getPeak(); - } - - public double getPeakDataReceiptRate() - { - return registry.getDataReceiptStatistics().getPeak(); - } - - public double getMessageReceiptRate() - { - return registry.getMessageReceiptStatistics().getRate(); - } - - public double getDataReceiptRate() - { - return registry.getDataReceiptStatistics().getRate(); - } - - public long getTotalMessagesReceived() - { - return registry.getMessageReceiptStatistics().getTotal(); - } - - public long getTotalDataReceived() - { - return registry.getDataReceiptStatistics().getTotal(); - } - - public boolean isStatisticsEnabled() - { - return registry.isStatisticsEnabled(); - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/LogRecorder.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/LogRecorder.java new file mode 100644 index 0000000000..a1065319d3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/LogRecorder.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.logging; + +import java.util.Iterator; +import org.apache.log4j.Appender; +import org.apache.log4j.Layout; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.ErrorHandler; +import org.apache.log4j.spi.Filter; +import org.apache.log4j.spi.LoggingEvent; + +public class LogRecorder implements Appender, Iterable +{ + private ErrorHandler _errorHandler; + private Filter _filter; + private String _name; + private long _recordId; + + private final int _bufferSize = 4096; + private final int _mask = _bufferSize - 1; + private Record[] _records = new Record[_bufferSize]; + + + public static class Record + { + private final long _id; + private final String _logger; + private final long _timestamp; + private final String _threadName; + private final String _level; + private final String _message; + + + public Record(long id, LoggingEvent event) + { + _id = id; + _logger = event.getLoggerName(); + _timestamp = event.timeStamp; + _threadName = event.getThreadName(); + _level = event.getLevel().toString(); + _message = event.getRenderedMessage(); + } + + public long getId() + { + return _id; + } + + public long getTimestamp() + { + return _timestamp; + } + + public String getThreadName() + { + return _threadName; + } + + public String getLevel() + { + return _level; + } + + public String getMessage() + { + return _message; + } + + public String getLogger() + { + return _logger; + } + } + + public LogRecorder() + { + + Logger.getRootLogger().addAppender(this); + } + + @Override + public void addFilter(Filter filter) + { + _filter = filter; + } + + @Override + public void clearFilters() + { + _filter = null; + } + + @Override + public void close() + { + //TODO - Implement + } + + @Override + public synchronized void doAppend(LoggingEvent loggingEvent) + { + _records[((int) (_recordId & _mask))] = new Record(_recordId, loggingEvent); + _recordId++; + } + + @Override + public ErrorHandler getErrorHandler() + { + return _errorHandler; + } + + @Override + public Filter getFilter() + { + return _filter; + } + + @Override + public Layout getLayout() + { + return null; + } + + @Override + public String getName() + { + return _name; + } + + @Override + public boolean requiresLayout() + { + return false; + } + + @Override + public void setErrorHandler(ErrorHandler errorHandler) + { + _errorHandler = errorHandler; + } + + @Override + public void setLayout(Layout layout) + { + + } + + @Override + public void setName(String name) + { + _name = name; + } + + @Override + public Iterator iterator() + { + return new RecordIterator(Math.max(_recordId-_bufferSize, 0l)); + } + + private class RecordIterator implements Iterator + { + private long _id; + + public RecordIterator(long currentRecordId) + { + _id = currentRecordId; + } + + @Override + public boolean hasNext() + { + return _id < _recordId; + } + + @Override + public Record next() + { + Record record = _records[((int) (_id & _mask))]; + while(_id < _recordId-_bufferSize) + { + _id = _recordId-_bufferSize; + record = _records[((int) (_id & _mask))]; + } + _id++; + return record; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java deleted file mode 100644 index c699dff175..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java +++ /dev/null @@ -1,822 +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.logging.management; - -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.xml.Log4jEntityResolver; -import org.apache.log4j.xml.QpidLog4JConfigurator; -import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException; -import org.apache.log4j.xml.QpidLog4JConfigurator.QpidLog4JSaxErrorHandler; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXException; - -import org.apache.qpid.management.common.mbeans.LoggingManagement; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.management.AMQManagedObject; - -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.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import static org.apache.log4j.xml.QpidLog4JConfigurator.LOCK; - - -/** MBean class for BrokerLoggingManagerMBean. It implements all the management features exposed for managing logging. */ -@MBeanDescription("Logging Management Interface") -public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement -{ - - private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class); - private String _log4jConfigFileName; - private int _log4jLogWatchInterval; - private static final String INHERITED = "INHERITED"; - private static final String[] LEVELS = new String[]{Level.ALL.toString(), Level.TRACE.toString(), - Level.DEBUG.toString(), Level.INFO.toString(), - Level.WARN.toString(), Level.ERROR.toString(), - Level.FATAL.toString(),Level.OFF.toString(), - INHERITED}; - private static TabularType _loggerLevelTabularType; - private static CompositeType _loggerLevelCompositeType; - - static - { - try - { - OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING}; - - _loggerLevelCompositeType = 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); - - _loggerLevelTabularType = new TabularType("LoggerLevel", "List of loggers with levels", - _loggerLevelCompositeType, - TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); - } - catch (OpenDataException e) - { - _logger.error("Tabular data setup for viewing logger levels was incorrect."); - _loggerLevelTabularType = null; - } - } - - public LoggingManagementMBean(String log4jConfigFileName, int log4jLogWatchInterval) throws JMException - { - super(LoggingManagement.class, LoggingManagement.TYPE); - _log4jConfigFileName = log4jConfigFileName; - _log4jLogWatchInterval = log4jLogWatchInterval; - } - - public String getObjectInstanceName() - { - return LoggingManagement.TYPE; - } - - public Integer getLog4jLogWatchInterval() - { - return _log4jLogWatchInterval; - } - - public String[] getAvailableLoggerLevels() - { - return LEVELS; - } - @SuppressWarnings("unchecked") - public synchronized boolean setRuntimeLoggerLevel(String logger, String level) - { - //check specified level is valid - Level newLevel; - try - { - newLevel = getLevel(level); - } - catch (Exception e) - { - return false; - } - - //check specified logger exists - Enumeration loggers = LogManager.getCurrentLoggers(); - Boolean loggerExists = false; - - while(loggers.hasMoreElements()) - { - Logger log = (Logger) loggers.nextElement(); - if (log.getName().equals(logger)) - { - loggerExists = true; - break; - } - } - - if(!loggerExists) - { - return false; - } - - //set the logger to the new level - _logger.info("Setting level to " + level + " for logger: " + logger); - - Logger log = Logger.getLogger(logger); - log.setLevel(newLevel); - - return true; - } - - @SuppressWarnings("unchecked") - public synchronized TabularData viewEffectiveRuntimeLoggerLevels() - { - if (_loggerLevelTabularType == null) - { - _logger.warn("TabluarData type not set up correctly"); - return null; - } - - _logger.info("Getting levels for currently active log4j loggers"); - - Enumeration loggers = LogManager.getCurrentLoggers(); - - TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType); - - Logger logger; - String loggerName; - String level; - - try - { - while(loggers.hasMoreElements()){ - logger = (Logger) loggers.nextElement(); - - loggerName = logger.getName(); - level = logger.getEffectiveLevel().toString(); - - Object[] itemData = {loggerName, level}; - CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); - loggerLevelList.put(loggerData); - } - } - catch (OpenDataException e) - { - _logger.warn("Unable to create logger level list due to :" + e); - return null; - } - - return loggerLevelList; - - } - - public synchronized String getRuntimeRootLoggerLevel() - { - Logger rootLogger = Logger.getRootLogger(); - - return rootLogger.getLevel().toString(); - } - - public synchronized boolean setRuntimeRootLoggerLevel(String level) - { - Level newLevel; - try - { - newLevel = getLevel(level); - } - catch (Exception e) - { - return false; - } - - if(newLevel == null) - { - //A null Level reference implies inheritance. Setting the runtime RootLogger - //to null is catastrophic (and prevented by Log4J at startup and runtime anyway). - return false; - } - - _logger.info("Setting RootLogger level to " + level); - - Logger log = Logger.getRootLogger(); - log.setLevel(newLevel); - - return true; - } - - //method to convert from a string to a log4j Level, throws exception if the given value is invalid - private Level getLevel(String level) throws Exception - { - if("null".equalsIgnoreCase(level) || INHERITED.equalsIgnoreCase(level)) - { - //the string "null" or "inherited" signals to inherit from a parent logger, - //using a null Level reference for the logger. - return null; - } - - Level newLevel = Level.toLevel(level); - - //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result. - if (newLevel.equals(Level.DEBUG) && !(level.equalsIgnoreCase("debug"))) - { - //received DEBUG but we did not ask for it, the Level request failed. - throw new Exception("Invalid level name"); - } - - return newLevel; - } - - //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content. - private static synchronized Document parseConfigFile(String fileName) throws IOException - { - try - { - LOCK.lock(); - - //check file was specified, exists, and is readable - if(fileName == null) - { - _logger.warn("Provided log4j XML configuration filename is null"); - throw new IOException("Provided log4j XML configuration filename is null"); - } - - File configFile = new File(fileName); - - if (!configFile.exists()) - { - _logger.warn("The log4j XML configuration file could not be found: " + fileName); - throw new IOException("The log4j XML configuration file could not be found"); - } - else if (!configFile.canRead()) - { - _logger.warn("The log4j XML configuration file is not readable: " + fileName); - throw new IOException("The log4j XML configuration file is not readable"); - } - - //parse it - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder; - Document doc; - - ErrorHandler errHandler = new QpidLog4JSaxErrorHandler(); - try - { - docFactory.setValidating(true); - docBuilder = docFactory.newDocumentBuilder(); - docBuilder.setErrorHandler(errHandler); - docBuilder.setEntityResolver(new Log4jEntityResolver()); - doc = docBuilder.parse(fileName); - } - catch (ParserConfigurationException e) - { - _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e); - //recommended that MBeans should use java.* and javax.* exceptions only - throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage()); - } - catch (SAXException e) - { - _logger.warn("The specified log4j XML file is invalid: " + e); - //recommended that MBeans should use standard java.* and javax.* exceptions only - throw new IOException("The specified log4j XML file is invalid: " + e.getMessage()); - } - catch (IOException e) - { - _logger.warn("Unable to parse the specified log4j XML file" + e); - throw new IOException("Unable to parse the specified log4j XML file: " + e.getMessage()); - } - - return doc; - } - finally - { - LOCK.unlock(); - } - } - - - private static synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException - { - try - { - LOCK.lock(); - - File log4jConfigFile = new File(log4jConfigFileName); - - if (!log4jConfigFile.canWrite()) - { - _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile); - throw new IOException("Specified log4j XML configuration file is not writable"); - } - - Transformer transformer = null; - try - { - transformer = TransformerFactory.newInstance().newTransformer(); - } - catch (Exception e) - { - _logger.warn("Could not create an XML transformer: " +e); - return false; - } - - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd"); - DOMSource source = new DOMSource(doc); - - File tmp; - Random r = new Random(); - do - { - tmp = new File(log4jConfigFile.getPath() + r.nextInt() + ".tmp"); - } - while(tmp.exists()); - - tmp.deleteOnExit(); - - try - { - StreamResult result = new StreamResult(tmp); - transformer.transform(source, result); - } - catch (TransformerException e) - { - _logger.warn("Could not transform the XML into new file: " +e); - throw new IOException("Could not transform the XML into new file: " +e); - } - - // Swap temp file in to replace existing configuration file. - File old = new File(log4jConfigFile.getAbsoluteFile() + ".old"); - if (old.exists()) - { - old.delete(); - } - - if(!log4jConfigFile.renameTo(old)) - { - //unable to rename the existing file to the backup name - _logger.error("Could not backup the existing log4j XML file"); - throw new IOException("Could not backup the existing log4j XML file"); - } - - if(!tmp.renameTo(log4jConfigFile)) - { - //failed to rename the new file to the required filename - - if(!old.renameTo(log4jConfigFile)) - { - //unable to return the backup to required filename - _logger.error("Could not rename the new log4j configuration file into place, and unable to restore original file"); - throw new IOException("Could not rename the new log4j configuration file into place, and unable to restore original file"); - } - - _logger.error("Could not rename the new log4j configuration file into place"); - throw new IOException("Could not rename the new log4j configuration file into place"); - } - - return true; - } - finally - { - LOCK.unlock(); - } - } - - - /* The log4j XML configuration file DTD defines three possible element - * combinations for specifying optional logger+level settings. - * Must account for the following: - * - * OR - * OR - * - * - * Noting also that the level/priority child element is optional too, - * and not the only possible child element. - */ - - public static synchronized Map retrieveConfigFileLoggersLevels(String fileName) throws IOException - { - try - { - LOCK.lock(); - - Document doc = parseConfigFile(fileName); - - HashMap loggerLevelList = new HashMap(); - - //retrieve the 'category' element nodes - NodeList categoryElements = doc.getElementsByTagName("category"); - - String categoryName; - String priority = null; - - for (int i = 0; i < categoryElements.getLength(); i++) - { - Element categoryElement = (Element) categoryElements.item(i); - categoryName = categoryElement.getAttribute("name"); - - //retrieve the category's mandatory 'priority' or 'level' element's value. - //It may not be the only child node, so request by tag name. - NodeList priorityElements = categoryElement.getElementsByTagName("priority"); - NodeList levelElements = categoryElement.getElementsByTagName("level"); - - if (priorityElements.getLength() != 0) - { - Element priorityElement = (Element) priorityElements.item(0); - priority = priorityElement.getAttribute("value"); - } - else if (levelElements.getLength() != 0) - { - Element levelElement = (Element) levelElements.item(0); - priority = levelElement.getAttribute("value"); - } - else - { - //there is no exiting priority or level to view, move onto next category/logger - continue; - } - - loggerLevelList.put(categoryName, priority); - } - - //retrieve the 'logger' element nodes - NodeList loggerElements = doc.getElementsByTagName("logger"); - - String loggerName; - String level; - - for (int i = 0; i < loggerElements.getLength(); i++) - { - Element loggerElement = (Element) loggerElements.item(i); - loggerName = loggerElement.getAttribute("name"); - - //retrieve the logger's mandatory 'level' element's value - //It may not be the only child node, so request by tag name. - NodeList levelElements = loggerElement.getElementsByTagName("level"); - - Element levelElement = (Element) levelElements.item(0); - level = levelElement.getAttribute("value"); - - loggerLevelList.put(loggerName, level); - } - - return loggerLevelList; - } - finally - { - LOCK.unlock(); - } - } - - public synchronized TabularData viewConfigFileLoggerLevels() throws IOException - { - try - { - LOCK.lock(); - - if (_loggerLevelTabularType == null) - { - _logger.warn("TabluarData type not set up correctly"); - return null; - } - - _logger.info("Getting logger levels from log4j configuration file"); - - TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType); - - Map levels = retrieveConfigFileLoggersLevels(_log4jConfigFileName); - - for (Map.Entry entry : levels.entrySet()) - { - String loggerName = entry.getKey(); - String level = entry.getValue(); - - try - { - Object[] itemData = {loggerName, level.toUpperCase()}; - CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, - COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); - loggerLevelList.put(loggerData); - } - catch (OpenDataException e) - { - _logger.warn("Unable to create logger level list due to :" + e); - return null; - } - } - - return loggerLevelList; - } - finally - { - LOCK.unlock(); - } - } - - public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException - { - try - { - LOCK.lock(); - - //check that the specified level is a valid log4j Level - try - { - getLevel(level); - } - catch (Exception e) - { - //it isnt a valid level - return false; - } - - _logger.info("Setting level to " + level + " for logger '" + logger - + "' in log4j xml configuration file: " + _log4jConfigFileName); - - Document doc = parseConfigFile(_log4jConfigFileName); - - //retrieve the 'category' and 'logger' element nodes - NodeList categoryElements = doc.getElementsByTagName("category"); - NodeList loggerElements = doc.getElementsByTagName("logger"); - - //collect them into a single elements list - List logElements = new ArrayList(); - - for (int i = 0; i < categoryElements.getLength(); i++) - { - logElements.add((Element) categoryElements.item(i)); - } - for (int i = 0; i < loggerElements.getLength(); i++) - { - logElements.add((Element) loggerElements.item(i)); - } - - //try to locate the specified logger/category in the elements retrieved - Element logElement = null; - for (Element e : logElements) - { - if (e.getAttribute("name").equals(logger)) - { - logElement = e; - break; - } - } - - if (logElement == null) - { - //no loggers/categories with given name found, does not exist to update - _logger.warn("Specified logger does not exist in the configuration file: " +logger); - return false; - } - - //retrieve the optional 'priority' or 'level' sub-element value. - //It may not be the only child node, so request by tag name. - NodeList priorityElements = logElement.getElementsByTagName("priority"); - NodeList levelElements = logElement.getElementsByTagName("level"); - - Element levelElement = null; - if (priorityElements.getLength() != 0) - { - levelElement = (Element) priorityElements.item(0); - } - else if (levelElements.getLength() != 0) - { - levelElement = (Element) levelElements.item(0); - } - else - { - //there is no exiting priority or level element to update - return false; - } - - //update the element with the new level/priority - levelElement.setAttribute("value", level.toLowerCase()); - - //output the new file - return writeUpdatedConfigFile(_log4jConfigFileName, doc); - } - finally - { - LOCK.unlock(); - } - } - - - /* The log4j XML configuration file DTD defines 2 possible element - * combinations for specifying the optional root logger level settings - * Must account for the following: - * - * OR - * - * - * Noting also that the level/priority child element is optional too, - * and not the only possible child element. - */ - - public static synchronized String retrieveConfigFileRootLoggerLevel(String fileName) throws IOException - { - try - { - LOCK.lock(); - - Document doc = parseConfigFile(fileName); - - //retrieve the optional 'root' element node - NodeList rootElements = doc.getElementsByTagName("root"); - - if (rootElements.getLength() == 0) - { - //there is no root logger definition - return "N/A"; - } - - Element rootElement = (Element) rootElements.item(0); - - //retrieve the optional 'priority' or 'level' element value. - //It may not be the only child node, so request by tag name. - NodeList priorityElements = rootElement.getElementsByTagName("priority"); - NodeList levelElements = rootElement.getElementsByTagName("level"); - String priority = null; - - if (priorityElements.getLength() != 0) - { - Element priorityElement = (Element) priorityElements.item(0); - priority = priorityElement.getAttribute("value"); - } - else if(levelElements.getLength() != 0) - { - Element levelElement = (Element) levelElements.item(0); - priority = levelElement.getAttribute("value"); - } - - if(priority != null) - { - return priority; - } - else - { - return "N/A"; - } - } - finally - { - LOCK.unlock(); - } - } - - public synchronized String getConfigFileRootLoggerLevel() throws IOException - { - return retrieveConfigFileRootLoggerLevel(_log4jConfigFileName).toUpperCase(); - } - - public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException - { - try - { - LOCK.lock(); - - //check that the specified level is a valid log4j Level - try - { - Level newLevel = getLevel(level); - if(newLevel == null) - { - //A null Level reference implies inheritance. Setting the config file RootLogger - //to "null" or "inherited" just ensures it defaults to DEBUG at startup as Log4J - //prevents this catastrophic situation at startup and runtime anyway. - return false; - } - } - catch (Exception e) - { - //it isnt a valid level - return false; - } - - _logger.info("Setting level to " + level + " for the Root logger in " + - "log4j xml configuration file: " + _log4jConfigFileName); - - Document doc = parseConfigFile(_log4jConfigFileName); - - //retrieve the optional 'root' element node - NodeList rootElements = doc.getElementsByTagName("root"); - - if (rootElements.getLength() == 0) - { - return false; - } - - Element rootElement = (Element) rootElements.item(0); - - //retrieve the optional 'priority' or 'level' sub-element value. - //It may not be the only child node, so request by tag name. - NodeList priorityElements = rootElement.getElementsByTagName("priority"); - NodeList levelElements = rootElement.getElementsByTagName("level"); - - Element levelElement = null; - if (priorityElements.getLength() != 0) - { - levelElement = (Element) priorityElements.item(0); - } - else if (levelElements.getLength() != 0) - { - levelElement = (Element) levelElements.item(0); - } - else - { - //there is no exiting priority/level to update - return false; - } - - //update the element with the new level/priority - levelElement.setAttribute("value", level); - - //output the new file - return writeUpdatedConfigFile(_log4jConfigFileName, doc); - } - finally - { - LOCK.unlock(); - } - } - - public synchronized void reloadConfigFile() throws IOException - { - try - { - LOCK.lock(); - - QpidLog4JConfigurator.configure(_log4jConfigFileName); - _logger.info("Applied log4j configuration from: " + _log4jConfigFileName); - } - catch (IllegalLoggerLevelException e) - { - _logger.warn("The log4j configuration reload request was aborted: " + e); - //recommended that MBeans should use standard java.* and javax.* exceptions only - throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); - } - catch (ParserConfigurationException e) - { - _logger.warn("The log4j configuration reload request was aborted: " + e); - throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); - } - catch (SAXException e) - { - _logger.warn("The log4j configuration reload request was aborted: " + e); - //recommended that MBeans should use standard java.* and javax.* exceptions only - throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); - } - catch (IOException e) - { - _logger.warn("The log4j configuration reload request was aborted: " + e); - throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage()); - } - finally - { - LOCK.unlock(); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java deleted file mode 100644 index 5c57c01f6e..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java +++ /dev/null @@ -1,105 +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.management; - -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; - -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 additinal feature of Notification Broadcaster to the - * DefaultManagedObject. - * @author Bhupendra Bhardwaj - * @version 0.1 - */ -public abstract class AMQManagedObject extends DefaultManagedObject - implements NotificationBroadcaster -{ - private NotificationBroadcasterSupport _broadcaster = new NotificationBroadcasterSupport(); - - private long _notificationSequenceNumber = 0; - - private LogActor _logActor; - - protected AMQManagedObject(Class managementInterface, String typeName) - throws NotCompliantMBeanException - { - super(managementInterface, typeName); - // CurrentActor will be defined as these objects are created during - // broker startup. - _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger()); - } - - // 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; - } - - /** - * sequence number for notifications - */ - protected long getNotificationSequenceNumber() - { - return _notificationSequenceNumber; - } - - protected void setNotificationSequenceNumber(long notificationSequenceNumber) - { - _notificationSequenceNumber = notificationSequenceNumber; - } - - protected long incrementAndGetSequenceNumber() - { - return ++_notificationSequenceNumber; - } - - protected LogActor getLogActor() - { - return _logActor; - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java deleted file mode 100644 index 6cfc827046..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.management; - -import org.apache.qpid.management.common.mbeans.ManagedConnection; - -import javax.management.JMException; -import javax.management.MBeanNotificationInfo; -import javax.management.NotCompliantMBeanException; -import javax.management.Notification; -import javax.management.ObjectName; -import javax.management.monitor.MonitorNotification; -import javax.management.openmbean.CompositeType; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.SimpleType; -import javax.management.openmbean.TabularType; - -public abstract class AbstractAMQManagedConnectionObject extends AMQManagedObject implements ManagedConnection -{ - private final String _name; - - protected static final OpenType[] _channelAttributeTypes = { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN }; - protected static final CompositeType _channelType; - protected static final TabularType _channelsType; - - protected static final String BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR = - "Broker Management Console has closed the connection."; - - static - { - try - { - _channelType = new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), - COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), _channelAttributeTypes); - _channelsType = new TabularType("Channels", "Channels", _channelType, (String[]) TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); - } - catch (JMException ex) - { - // This is not expected to ever occur. - throw new RuntimeException("Got JMException in static initializer.", ex); - } - } - - protected AbstractAMQManagedConnectionObject(final String remoteAddress) throws NotCompliantMBeanException - { - super(ManagedConnection.class, ManagedConnection.TYPE); - _name = "anonymous".equals(remoteAddress) ? (remoteAddress + hashCode()) : remoteAddress; - } - - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } - - public void notifyClients(String notificationMsg) - { - final Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, incrementAndGetSequenceNumber(), - System.currentTimeMillis(), notificationMsg); - getBroadcaster().sendNotification(n); - } - - @Override - public MBeanNotificationInfo[] getNotificationInfo() - { - String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; - String name = MonitorNotification.class.getName(); - String description = "Channel count has reached threshold value"; - MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); - - return new MBeanNotificationInfo[] { info1 }; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java deleted file mode 100644 index 10d7503800..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java +++ /dev/null @@ -1,192 +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.management; - -import org.apache.log4j.Logger; - -import org.apache.qpid.server.registry.ApplicationRegistry; - -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) - throws NotCompliantMBeanException - { - super(managementInterface); - _managementInterface = managementInterface; - _typeName = typeName; - _mbeanInfo = buildMBeanInfo(); - } - - @Override - public MBeanInfo getMBeanInfo() - { - return _mbeanInfo; - } - - public String getType() - { - return _typeName; - } - - public Class getManagementInterface() - { - return _managementInterface; - } - - public ManagedObject getParentObject() - { - return null; - } - - public void register() throws JMException - { - _registry = ApplicationRegistry.getInstance().getManagedObjectRegistry(); - _registry.registerObject(this); - } - - public void unregister() - { - try - { - if(_registry != null) - { - _registry.unregisterObject(this); - } - } - catch (JMException e) - { - LOGGER.error("Error unregistering managed object: " + this + ": " + e, e); - } - finally - { - _registry = null; - } - } - - public String toString() - { - return getObjectInstanceName() + "[" + getType() + "]"; - } - - /** - * Created the ObjectName as per the JMX Specs - * @return ObjectName - * @throws 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/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java deleted file mode 100644 index 869a816cf1..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ /dev/null @@ -1,490 +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.management; - -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.security.Principal; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import javax.management.JMException; -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; -import javax.management.Notification; -import javax.management.NotificationFilterSupport; -import javax.management.NotificationListener; -import javax.management.ObjectName; -import javax.management.remote.JMXConnectionNotification; -import javax.management.remote.JMXConnectorServer; -import javax.management.remote.JMXServiceURL; -import javax.management.remote.MBeanServerForwarder; -import javax.management.remote.rmi.RMIConnection; -import javax.management.remote.rmi.RMIConnectorServer; -import javax.management.remote.rmi.RMIJRMPServerImpl; -import javax.management.remote.rmi.RMIServerImpl; -import javax.rmi.ssl.SslRMIClientSocketFactory; -import javax.rmi.ssl.SslRMIServerSocketFactory; -import javax.security.auth.Subject; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -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; - -/** - * 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(); - rmipa.setAuthenticationManager(appRegistry.getAuthenticationManager(new InetSocketAddress(_jmxPortRegistryServer))); - HashMap env = new HashMap(); - 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 connectionIdUsernameMap = new ConcurrentHashMap(); - final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env) - { - - /** - * Override makeClient so we can cache the username of the client in a Map keyed by connectionId. - * ConnectionId is guaranteed to be unique per client connection, according to the JMS spec. - * An instance of NotificationListener (mapCleanupListener) will be responsible for removing these Map - * entries. - * - * @see javax.management.remote.rmi.RMIJRMPServerImpl#makeClient(java.lang.String, javax.security.auth.Subject) - */ - @Override - protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException - { - final RMIConnection makeClient = super.makeClient(connectionId, subject); - final Principal principal = subject.getPrincipals().iterator().next(); - connectionIdUsernameMap.put(connectionId, principal.getName()); - return makeClient; - } - }; - - // Create a Listener responsible for removing the map entries add by the #makeClient entry above. - final NotificationListener mapCleanupListener = new NotificationListener() - { - - @Override - public void handleNotification(Notification notification, Object handback) - { - final String connectionId = ((JMXConnectionNotification) notification).getConnectionId(); - connectionIdUsernameMap.remove(connectionId); - } - }; - - String localHost; - try - { - 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) - { - //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() - { - 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.getMessage()); - } - } - - if (_rmiRegistry != null) - { - // Stopping the RMI registry - CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _jmxPortRegistryServer)); - try - { - UnicastRemoteObject.unexportObject(_rmiRegistry, false); - } - catch (NoSuchObjectException e) - { - _log.error("Exception while closing the RMI Registry: " + e.getMessage()); - } - } - - //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/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java deleted file mode 100644 index 89b74f939d..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java +++ /dev/null @@ -1,399 +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.management; - -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 NotCompliantMBeanException - */ - static MBeanAttributeInfo[] getMBeanAttributesInfo(Class interfaceClass) - throws NotCompliantMBeanException - { - List attributesList = new ArrayList(); - - /** - * 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 operationsList = new ArrayList(); - - 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 NotCompliantMBeanException - */ - private static int getIndexIfAlreadyExists(MBeanAttributeInfo attribute, - List 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 constructors = new ArrayList(); - - 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/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java deleted file mode 100644 index 651372db16..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java +++ /dev/null @@ -1,367 +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.management; - -import org.apache.log4j.Logger; - -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.access.Operation; - -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 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 - { - // Actually invoke the method - return method.invoke(_mbs, args); - } - finally - { - if(_managementRightsInferAllAccess) - { - SecurityManager.setAccessChecksDisabled(oldAccessChecksDisabled); - } - } - } - catch (InvocationTargetException e) - { - throw e.getTargetException(); - } - } - - 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 connectionIdUsernameMap = (Map) 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/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java deleted file mode 100644 index 166a2a376d..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java +++ /dev/null @@ -1,34 +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.management; - -/** - * Any object that can return a related MBean should implement this interface. - * - * This enables other classes to get the managed object, which in turn is useful when - * constructing relationships between managed objects without having to maintain - * separate data structures containing MBeans. - * - */ -public interface Managable -{ - ManagedObject getManagedObject(); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java deleted file mode 100644 index 483b325455..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.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.server.management; - -import org.apache.qpid.AMQException; - -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 AMQException, JMException; - - void unregister() throws AMQException; - - /** - * Returns the ObjectName required for the mbeanserver registration. - * @return ObjectName - * @throws MalformedObjectNameException - */ - ObjectName getObjectName() throws MalformedObjectNameException; -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java deleted file mode 100644 index b3323c569c..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java +++ /dev/null @@ -1,49 +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.management; - -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/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java deleted file mode 100644 index e77350c3e4..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java +++ /dev/null @@ -1,58 +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.management; - -import org.apache.log4j.Logger; - -import javax.management.JMException; - -/** - * This managed object registry does not actually register MBeans. This can be used in tests when management is - * not required or when management has been disabled. - * - */ -public class NoopManagedObjectRegistry implements ManagedObjectRegistry -{ - private static final Logger _log = Logger.getLogger(NoopManagedObjectRegistry.class); - - public NoopManagedObjectRegistry() - { - _log.info("Management is disabled"); - } - - public void start() - { - //no-op - } - - public void registerObject(ManagedObject managedObject) throws JMException - { - } - - public void unregisterObject(ManagedObject managedObject) throws JMException - { - } - - public void close() - { - - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessageHeader.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessageHeader.java index faac14f8a7..63bd1e45a0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessageHeader.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessageHeader.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.message; +import java.util.Collection; import java.util.Set; public interface AMQMessageHeader @@ -28,6 +29,10 @@ public interface AMQMessageHeader long getExpiration(); + String getUserId(); + + String getAppId(); + String getMessageId(); String getMimeType(); @@ -52,4 +57,5 @@ public interface AMQMessageHeader boolean containsHeader(String name); + Collection getHeaderNames(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java index e87b67d242..01c1021070 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.message; +import java.util.Collection; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; @@ -50,6 +51,16 @@ public class ContentHeaderBodyAdapter implements AMQMessageHeader return getProperties().getExpiration(); } + public String getUserId() + { + return getProperties().getUserIdAsString(); + } + + public String getAppId() + { + return getProperties().getAppIdAsString(); + } + public String getMessageId() { return getProperties().getMessageIdAsString(); @@ -117,6 +128,13 @@ public class ContentHeaderBodyAdapter implements AMQMessageHeader return true; } + @Override + public Collection getHeaderNames() + { + FieldTable ft = getProperties().getHeaders(); + return ft.keys(); + } + public boolean containsHeader(String name) { FieldTable ft = getProperties().getHeaders(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java index 583f0c09a7..e890bf5ef8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.message; +import java.util.Collection; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -242,6 +243,16 @@ public class MessageMetaData implements StorableMessageMetaData return (BasicContentHeaderProperties) getContentHeaderBody().getProperties(); } + public String getUserId() + { + return getProperties().getUserIdAsString(); + } + + public String getAppId() + { + return getProperties().getAppIdAsString(); + } + public String getCorrelationId() { return getProperties().getCorrelationIdAsString(); @@ -318,6 +329,12 @@ public class MessageMetaData implements StorableMessageMetaData return true; } + @Override + public Collection getHeaderNames() + { + return getProperties().getHeaders().keys(); + } + public boolean containsHeader(String name) { FieldTable ft = getProperties().getHeaders(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java index 7d030fe711..2cc1a92853 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java @@ -21,11 +21,7 @@ package org.apache.qpid.server.message; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import org.apache.qpid.amqp_1_0.codec.ValueHandler; import org.apache.qpid.amqp_1_0.messaging.SectionDecoder; import org.apache.qpid.amqp_1_0.type.AmqpErrorException; @@ -486,6 +482,18 @@ public class MessageMetaData_1_0 implements StorableMessageMetaData return null; //TODO } + public String getAppId() + { + //TODO + return null; + } + + public String getUserId() + { + // TODO + return null; + } + public Object getHeader(final String name) { return _appProperties == null ? null : _appProperties.get(name); @@ -508,6 +516,16 @@ public class MessageMetaData_1_0 implements StorableMessageMetaData return true; } + @Override + public Collection getHeaderNames() + { + if(_appProperties == null) + { + return Collections.emptySet(); + } + return Collections.unmodifiableCollection(_appProperties.keySet()); + } + public boolean containsHeader(final String name) { return _appProperties != null && _appProperties.containsKey(name); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java index 126e7c28cb..91384f7c22 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java @@ -20,14 +20,11 @@ */ package org.apache.qpid.server.message; +import java.util.*; import org.apache.qpid.transport.DeliveryProperties; import org.apache.qpid.transport.MessageDeliveryPriority; import org.apache.qpid.transport.MessageProperties; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - class MessageTransferHeader implements AMQMessageHeader { @@ -60,10 +57,22 @@ class MessageTransferHeader implements AMQMessageHeader return _deliveryProps == null ? 0L : _deliveryProps.getExpiration(); } + public String getUserId() + { + byte[] userIdBytes = _messageProps == null ? null : _messageProps.getUserId(); + return userIdBytes == null ? null : new String(userIdBytes); + } + + public String getAppId() + { + byte[] appIdBytes = _messageProps == null ? null : _messageProps.getAppId(); + return appIdBytes == null ? null : new String(appIdBytes); + } + public String getMessageId() { UUID id = _messageProps == null ? null : _messageProps.getMessageId(); - + return id == null ? null : String.valueOf(id); } @@ -93,7 +102,7 @@ class MessageTransferHeader implements AMQMessageHeader public String getType() { Object type = getHeader(JMS_TYPE); - return type instanceof String ? (String) type : null; + return type instanceof String ? (String) type : null; } public String getReplyTo() @@ -145,6 +154,14 @@ class MessageTransferHeader implements AMQMessageHeader } + @Override + public Collection getHeaderNames() + { + Map appHeaders = _messageProps == null ? null : _messageProps.getApplicationHeaders(); + return appHeaders != null ? Collections.unmodifiableCollection(appHeaders.keySet()) : Collections.EMPTY_SET ; + + } + public boolean containsHeader(String name) { Map appHeaders = _messageProps == null ? null : _messageProps.getApplicationHeaders(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Attribute.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Attribute.java new file mode 100644 index 0000000000..4fccf47e0e --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Attribute.java @@ -0,0 +1,199 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +public abstract class Attribute +{ + private final String _name; + public Attribute(String name) + { + _name = name; + } + + public String getName() + { + return _name; + } + + abstract public Class getType(); + + public T getValue(C configuredObject) + { + Object o = configuredObject.getAttribute(_name); + if(getType().isInstance(o)) + { + return (T) o; + } + return null; + } + + public T setValue(T expected, T desired, C configuredObject) + { + return (T) configuredObject.setAttribute(_name, expected, desired); + } + + abstract public T setValue(String stringValue, C configuredObject); + + static class StringAttribute extends Attribute + { + + public StringAttribute(String name) + { + super(name); + } + + @Override + public Class getType() + { + return String.class; + } + + @Override + public String setValue(String stringValue, C configuredObject) + { + return setValue(getValue(configuredObject), stringValue, configuredObject); + } + + } + + static class IntegerAttribute extends Attribute + { + + public IntegerAttribute(String name) + { + super(name); + } + + @Override + public Class getType() + { + return Integer.class; + } + + @Override + public Integer setValue(String stringValue, C configuredObject) + { + try + { + Integer val = Integer.valueOf(stringValue); + return setValue(getValue(configuredObject), val, configuredObject); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException(e); + } + } + } + + + static class LongAttribute extends Attribute + { + + public LongAttribute(String name) + { + super(name); + } + + @Override + public Class getType() + { + return Long.class; + } + + @Override + public Long setValue(String stringValue, C configuredObject) + { + try + { + Long val = Long.valueOf(stringValue); + return setValue(getValue(configuredObject), val, configuredObject); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException(e); + } + } + } + + + static class DoubleAttribute extends Attribute + { + + public DoubleAttribute(String name) + { + super(name); + } + + @Override + public Class getType() + { + return Double.class; + } + + @Override + public Double setValue(String stringValue, C configuredObject) + { + try + { + Double val = Double.valueOf(stringValue); + return setValue(getValue(configuredObject), val, configuredObject); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException(e); + } + } + } + + + static class FloatAttribute extends Attribute + { + + public FloatAttribute(String name) + { + super(name); + } + + @Override + public Class getType() + { + return Float.class; + } + + @Override + public Float setValue(String stringValue, C configuredObject) + { + try + { + Float val = Float.valueOf(stringValue); + return setValue(getValue(configuredObject), val, configuredObject); + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException(e); + } + } + } + + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationMethod.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationMethod.java new file mode 100644 index 0000000000..7a5927a365 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationMethod.java @@ -0,0 +1,33 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.Collection; + +public interface AuthenticationMethod extends ConfiguredObject +{ + // name is the SASL mech where this is a SASL authentication + + // parents + VirtualHostAlias getVirtualHostAlias(); + AuthenticationProvider getAuthenticationProvider(); + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java new file mode 100644 index 0000000000..6000886956 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java @@ -0,0 +1,55 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface AuthenticationProvider extends ConfiguredObject +{ + + public static final String ID = "id"; + public static final String DESCRIPTION = "description"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + public static final String TYPE = "type"; + + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(ID, + NAME, + DESCRIPTION, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + TYPE)); + //children + Collection getVirtualHostPortBindings(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java new file mode 100644 index 0000000000..ca1de0f189 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.security.AccessControlException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface Broker extends ConfiguredObject +{ + + String BUILD_VERSION = "buildVersion"; + String BYTES_RETAINED = "bytesRetained"; + String OPERATING_SYSTEM = "operatingSystem"; + String PLATFORM = "platform"; + String PROCESS_PID = "processPid"; + String PRODUCT_VERSION = "productVersion"; + String STATISTICS_ENABLED = "statisticsEnabled"; + String SUPPORTED_STORE_TYPES = "supportedStoreTypes"; + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(BUILD_VERSION, + BYTES_RETAINED, + OPERATING_SYSTEM, + PLATFORM, + PROCESS_PID, + PRODUCT_VERSION, + STATISTICS_ENABLED, + SUPPORTED_STORE_TYPES, + CREATED, + DURABLE, + ID, + LIFETIME_POLICY, + NAME, + STATE, + TIME_TO_LIVE, + UPDATED)); + + //children + Collection < VirtualHost > getVirtualHosts(); + + Collection getPorts(); + + Collection getAuthenticationProviders(); + + VirtualHost createVirtualHost(String name, State initialState, boolean durable, + LifetimePolicy lifetime, long ttl, Map attributes) + throws AccessControlException, IllegalArgumentException; + + void deleteVirtualHost(VirtualHost virtualHost) + throws AccessControlException, IllegalStateException; +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java index 6477633a9b..78b98faffe 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java @@ -30,7 +30,8 @@ public interface ConfigurationChangeListener * @param newState the state after the change */ void stateChanged(ConfiguredObject object, State oldState, State newState); - + + void childAdded(ConfiguredObject object, ConfiguredObject child); void childRemoved(ConfiguredObject object, ConfiguredObject child); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java index fb47a54d0a..414b2d083a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.model; import java.security.AccessControlException; import java.util.Collection; +import java.util.Map; import java.util.UUID; public interface ConfiguredObject @@ -81,7 +82,7 @@ public interface ConfiguredObject * @param desiredState the state the caller wishes the object to attain * @return the new current state * @throws IllegalStateTransitionException the requested state tranisition is invalid - * @throws AccessControlException the current context does not have sufficeint permissions to change the state + * @throws AccessControlException the current context does not have sufficient permissions to change the state */ State setDesiredState(State currentState, State desiredState) throws IllegalStateTransitionException, AccessControlException; @@ -89,7 +90,7 @@ public interface ConfiguredObject /** * Get the actual state of the object. * - * This state is derived fromt the desired state of the object itself and + * This state is derived from the desired state of the object itself and * the actual state of its parents. If an object "desires" to be ACTIVE, but one of its parents is STOPPED, then * the actual state of the object will be STOPPED * @@ -126,7 +127,7 @@ public interface ConfiguredObject /** * Returns whether the the object configuration is durably stored * - * @return the durablity + * @return the durability */ boolean isDurable(); @@ -188,7 +189,7 @@ public interface ConfiguredObject /** * Get the names of attributes that are set on this object * - * Not that the returned collection is correct at the time the method is called, but will not reflect future + * Note that the returned collection is correct at the time the method is called, but will not reflect future * additions or removals when they occur * * @return the collection of attribute names @@ -226,4 +227,20 @@ public interface ConfiguredObject * @return the Statistics holder for the ConfiguredObject (or null if none exists) */ Statistics getStatistics(); + + /** + * Return children of the ConfiguredObject of the given class + * + * @param clazz the class of the children to return + * @return the children + * + * @throws NullPointerException if the supplied class null + * + */ + Collection getChildren(Class clazz); + + + C createChild(Class childClass, + Map attributes, + ConfiguredObject... otherParents); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFinder.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFinder.java new file mode 100644 index 0000000000..6a7d6f8f7b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFinder.java @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.Collection; + +public class ConfiguredObjectFinder +{ + public static C findConfiguredObjectByName(Collection configuredObjects, String name) + { + for (C configuredObject : configuredObjects) + { + if (name.equals(configuredObject.getName())) + { + return configuredObject; + } + } + return null; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Connection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Connection.java new file mode 100644 index 0000000000..aaf6007afd --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Connection.java @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface Connection extends ConfiguredObject +{ + + // Statistics + + String BYTES_IN = "bytesIn"; + String BYTES_OUT = "bytesOut"; + String LAST_IO_TIME = "lastIoTime"; + String LOCAL_TRANSACTION_BEGINS = "localTransactionBegins"; + String LOCAL_TRANSACTION_ROLLBACKS = "localTransactionRollbacks"; + String MESSAGES_IN = "messagesIn"; + String MESSAGES_OUT = "messagesOut"; + String SESSION_COUNT = "sessionCount"; + String STATE_CHANGED = "stateChanged"; + String XA_TRANSACTION_BRANCH_ENDS = "xaTransactionBranchEnds"; + String XA_TRANSACTION_BRANCH_STARTS = "xaTransactionBranchStarts"; + String XA_TRANSACTION_BRANCH_SUSPENDS = "xaTransactionBranchSuspends"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableCollection( + Arrays.asList(BYTES_IN, + BYTES_OUT, + LAST_IO_TIME, + LOCAL_TRANSACTION_BEGINS, + LOCAL_TRANSACTION_ROLLBACKS, + MESSAGES_IN, + MESSAGES_OUT, + SESSION_COUNT, + STATE_CHANGED, + XA_TRANSACTION_BRANCH_ENDS, + XA_TRANSACTION_BRANCH_STARTS, + XA_TRANSACTION_BRANCH_SUSPENDS)); + + // Attributes + + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + + public static final String CLIENT_ID = "clientId"; + public static final String CLIENT_VERSION = "clientVersion"; + public static final String INCOMING = "incoming"; + public static final String LOCAL_ADDRESS = "localAddress"; + public static final String PRINCIPAL = "principal"; + public static final String PROPERTIES = "properties"; + public static final String REMOTE_ADDRESS = "remoteAddress"; + public static final String REMOTE_PROCESS_NAME = "remoteProcessName"; + public static final String REMOTE_PROCESS_PID = "remoteProcessPid"; + public static final String SESSION_COUNT_LIMIT = "sessionCountLimit"; + + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection( + Arrays.asList( ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + CLIENT_ID, + CLIENT_VERSION, + INCOMING, + LOCAL_ADDRESS, + PRINCIPAL, + PROPERTIES, + REMOTE_ADDRESS, + REMOTE_PROCESS_NAME, + REMOTE_PROCESS_PID, + SESSION_COUNT_LIMIT)); + + //children + Collection getSessions(); + + void delete(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Event.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Event.java new file mode 100644 index 0000000000..91b684f06e --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Event.java @@ -0,0 +1,27 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +abstract public class Event +{ + abstract public T getEventType(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/EventType.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/EventType.java new file mode 100644 index 0000000000..edd5ce4250 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/EventType.java @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * A type of event generated by a ConfiguredObject. + */ +public abstract class EventType> +{ + private static final Map, Integer> EVENT_TYPES = + new HashMap, Integer>(); + + private final int _classId; + + protected EventType() + { + synchronized (EVENT_TYPES) + { + if(EVENT_TYPES.containsKey(getClass())) + { + throw new IllegalArgumentException("Cannot define more one instance of the same EventType " + + getClass().getName()); + } + else + { + _classId = EVENT_TYPES.size(); + EVENT_TYPES.put(getClass(), _classId); + } + } + } + + public final int getId() + { + return _classId; + } + + abstract public Event newEvent(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java index e872273d05..e63c71e955 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java @@ -77,7 +77,7 @@ public interface Exchange extends ConfiguredObject //children Collection getBindings(); Collection getPublishers(); - + //operations Binding createBinding(String bindingKey, Queue queue, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java new file mode 100644 index 0000000000..fd429321c8 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java @@ -0,0 +1,97 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class Model +{ + private static final Map, Collection>> + PARENTS = new HashMap, Collection>>(); + + + private static final Map, Collection>> + CHILDREN = new HashMap, Collection>>(); + + static void addRelationship(Class parent, Class child) + { + Collection> parents = PARENTS.get(child); + if(parents == null) + { + parents = new ArrayList>(); + PARENTS.put(child, parents); + } + parents.add(parent); + + Collection> children = CHILDREN.get(parent); + if(children == null) + { + children = new ArrayList>(); + CHILDREN.put(parent, children); + } + children.add(child); + } + + static + { + addRelationship(Broker.class, VirtualHost.class); + addRelationship(Broker.class, Port.class); + addRelationship(Broker.class, AuthenticationProvider.class); + + addRelationship(VirtualHost.class, Exchange.class); + addRelationship(VirtualHost.class, Queue.class); + addRelationship(VirtualHost.class, Connection.class); + addRelationship(VirtualHost.class, VirtualHostAlias.class); + + addRelationship(AuthenticationProvider.class, User.class); + + addRelationship(Connection.class, Session.class); + + addRelationship(Exchange.class, Binding.class); + addRelationship(Exchange.class, Publisher.class); + + addRelationship(Queue.class, Binding.class); + addRelationship(Queue.class, Consumer.class); + + addRelationship(Session.class, Consumer.class); + addRelationship(Session.class, Publisher.class); + + } + + public static Collection> getParentTypes(Class child) + { + Collection> parentTypes = PARENTS.get(child); + return parentTypes == null ? Collections.EMPTY_LIST + : Collections.unmodifiableCollection(parentTypes); + } + + public static Collection> getChildTypes(Class parent) + { + Collection> childTypes = CHILDREN.get(parent); + return childTypes == null ? Collections.EMPTY_LIST + : Collections.unmodifiableCollection(childTypes); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/PasswordCredentialManagingAuthenticationProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/PasswordCredentialManagingAuthenticationProvider.java new file mode 100644 index 0000000000..1027e5ce8c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/PasswordCredentialManagingAuthenticationProvider.java @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.io.IOException; +import java.util.Map; + +import javax.security.auth.login.AccountNotFoundException; + +public interface PasswordCredentialManagingAuthenticationProvider extends AuthenticationProvider +{ + boolean createUser(String username, String password, Map attributes); + + void deleteUser(String user) throws AccountNotFoundException; + + void setPassword(String username, String password) throws AccountNotFoundException; + + Map> getUsers(); + + /** + * Refreshes the cache of user and password data from the underlying storage. + * + * If there is a failure whilst reloading the data, the implementation must + * throw an {@link IOException} and revert to using the previous cached username + * and password data. In this way, the broker will remain usable. + */ + void reload() throws IOException; +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java new file mode 100644 index 0000000000..50c0ebcd14 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java @@ -0,0 +1,91 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.security.AccessControlException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface Port extends ConfiguredObject +{ + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + String BINDING_ADDRESS = "bindingAddress"; + String PORT = "port"; + String PROTOCOLS = "protocols"; + String TRANSPORTS = "transports"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + BINDING_ADDRESS, + PORT, + PROTOCOLS, + TRANSPORTS + )); + + + String getBindingAddress(); + + int getPort(); + + Collection getTransports(); + + void addTransport(Transport transport) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + Transport removeTransport(Transport transport) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + + Collection getProtocols(); + + void addProtocol(Protocol protocol) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + Protocol removeProtocol(Protocol protocol) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + + //children + Collection getVirtualHostBindings(); + Collection getConnections(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Protocol.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Protocol.java new file mode 100644 index 0000000000..fecbcec194 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Protocol.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public enum Protocol +{ + AMQP_0_8, + AMQP_0_9, + AMQP_0_9_1, + AMQP_0_10, + AMQP_1_0, + JMX, + HTTP +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java index 7c4f0de22b..5dda4d66cd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java @@ -25,7 +25,6 @@ import java.util.Collection; import java.util.Collections; import org.apache.qpid.server.queue.QueueEntryVisitor; - public interface Queue extends ConfiguredObject { public static final String BINDING_COUNT = "bindingCount"; @@ -71,6 +70,7 @@ public interface Queue extends ConfiguredObject public static final String ID = "id"; + public static final String DESCRIPTION = "description"; public static final String NAME = "name"; public static final String STATE = "state"; public static final String DURABLE = "durable"; @@ -78,6 +78,7 @@ public interface Queue extends ConfiguredObject public static final String TIME_TO_LIVE = "timeToLive"; public static final String CREATED = "created"; public static final String UPDATED = "updated"; + public static final String ARGUMENTS = "arguments"; public static final String ALERT_REPEAT_GAP = "alertRepeatGap"; public static final String ALERT_THRESHOLD_MESSAGE_AGE = "alertThresholdMessageAge"; @@ -106,6 +107,7 @@ public interface Queue extends ConfiguredObject Collections.unmodifiableList( Arrays.asList(ID, NAME, + DESCRIPTION, STATE, DURABLE, LIFETIME_POLICY, @@ -143,4 +145,6 @@ public interface Queue extends ConfiguredObject void visit(QueueEntryVisitor visitor); void delete(); + + void setNotificationListener(QueueNotificationListener listener); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/QueueNotificationListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/QueueNotificationListener.java new file mode 100644 index 0000000000..ab601f685c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/QueueNotificationListener.java @@ -0,0 +1,28 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import org.apache.qpid.server.queue.NotificationCheck; + +public interface QueueNotificationListener +{ + void notifyClients(NotificationCheck notification, Queue queue, String notificationMsg); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Session.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Session.java new file mode 100644 index 0000000000..e813d0c129 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Session.java @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface Session extends ConfiguredObject +{ + // Statistics + + public static final String BYTES_IN = "bytesIn"; + public static final String BYTES_OUT = "bytesOut"; + public static final String CONSUMER_COUNT = "consumerCount"; + public static final String LOCAL_TRANSACTION_BEGINS = "localTransactionBegins"; + public static final String LOCAL_TRANSACTION_OPEN = "localTransactionOpen"; + public static final String LOCAL_TRANSACTION_ROLLBACKS = "localTransactionRollbacks"; + public static final String STATE_CHANGED = "stateChanged"; + public static final String UNACKNOWLEDGED_BYTES = "unacknowledgedBytes"; + public static final String UNACKNOWLEDGED_MESSAGES = "unacknowledgedMessages"; + public static final String XA_TRANSACTION_BRANCH_ENDS = "xaTransactionBranchEnds"; + public static final String XA_TRANSACTION_BRANCH_STARTS = "xaTransactionBranchStarts"; + public static final String XA_TRANSACTION_BRANCH_SUSPENDS = "xaTransactionBranchSuspends"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableCollection(Arrays.asList(BYTES_IN, BYTES_OUT, CONSUMER_COUNT, + LOCAL_TRANSACTION_BEGINS, + LOCAL_TRANSACTION_OPEN, + LOCAL_TRANSACTION_ROLLBACKS, STATE_CHANGED, + UNACKNOWLEDGED_BYTES, UNACKNOWLEDGED_MESSAGES, + XA_TRANSACTION_BRANCH_ENDS, XA_TRANSACTION_BRANCH_STARTS, + XA_TRANSACTION_BRANCH_SUSPENDS)); + + + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + + public static final String CHANNEL_ID = "channelId"; + // PRODUCER_FLOW_BLOCKED is exposed as an interim step. We will expose attribute(s) that exposing + // available credit of both producer and consumer sides. + public static final String PRODUCER_FLOW_BLOCKED = "producerFlowBlocked"; + + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection(Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + CHANNEL_ID, + PRODUCER_FLOW_BLOCKED)); + + Collection getSubscriptions(); + Collection getPublishers(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java index 2cb81eae82..92d6f47741 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java @@ -1,8 +1,4 @@ -package org.apache.qpid.server.model; - -import java.util.Collection; - -/** +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -18,6 +14,11 @@ import java.util.Collection; * See the License for the specific language governing permissions and * limitations under the License. */ + +package org.apache.qpid.server.model; + +import java.util.Collection; + public interface Statistics { Collection getStatisticNames(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Transport.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Transport.java new file mode 100644 index 0000000000..03cd46be01 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Transport.java @@ -0,0 +1,27 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +public enum Transport +{ + TCP, + SSL +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java index d8493c6df4..920088d61c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java @@ -40,11 +40,11 @@ public class UUIDGenerator return UUID.nameUUIDFromBytes(sb.toString().getBytes()); } - public static UUID generateExchangeUUID(String echangeName, String virtualHostName) + public static UUID generateExchangeUUID(String exchangeName, String virtualHostName) { - if(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString().equals(echangeName) || echangeName.startsWith("amq.") || echangeName.startsWith("qpid.")) + if(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString().equals(exchangeName) || exchangeName.startsWith("amq.") || exchangeName.startsWith("qpid.")) { - return generateUUID(echangeName, virtualHostName); + return generateUUID(exchangeName, virtualHostName); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java new file mode 100644 index 0000000000..d97bf46d31 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface User extends ConfiguredObject +{ + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + String PASSWORD = "password"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + PASSWORD + )); + + public String getPassword(); + + public void setPassword(String password); + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHost.java new file mode 100644 index 0000000000..53faefc954 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHost.java @@ -0,0 +1,154 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import org.apache.qpid.server.queue.QueueEntry; +import java.security.AccessControlException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface VirtualHost extends ConfiguredObject +{ + // Statistics + + public static final String BYTES_IN = "bytesIn"; + public static final String BYTES_OUT = "bytesOut"; + public static final String BYTES_RETAINED = "bytesRetained"; + public static final String LOCAL_TRANSACTION_BEGINS = "localTransactionBegins"; + public static final String LOCAL_TRANSACTION_ROLLBACKS = "localTransactionRollbacks"; + public static final String MESSAGES_IN = "messagesIn"; + public static final String MESSAGES_OUT = "messagesOut"; + public static final String MESSAGES_RETAINED = "messagesRetained"; + public static final String STATE_CHANGED = "stateChanged"; + public static final String XA_TRANSACTION_BRANCH_ENDS = "xaTransactionBranchEnds"; + public static final String XA_TRANSACTION_BRANCH_STARTS = "xaTransactionBranchStarts"; + public static final String XA_TRANSACTION_BRANCH_SUSPENDS = "xaTransactionBranchSuspends"; + public static final String QUEUE_COUNT = "queueCount"; + public static final String EXCHANGE_COUNT = "exchangeCount"; + public static final String CONNECTION_COUNT = "connectionCount"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableList( + Arrays.asList(BYTES_IN, BYTES_OUT, BYTES_RETAINED, LOCAL_TRANSACTION_BEGINS, + LOCAL_TRANSACTION_ROLLBACKS, MESSAGES_IN, MESSAGES_OUT, MESSAGES_RETAINED, STATE_CHANGED, + XA_TRANSACTION_BRANCH_ENDS, XA_TRANSACTION_BRANCH_STARTS, XA_TRANSACTION_BRANCH_SUSPENDS, + QUEUE_COUNT, EXCHANGE_COUNT, CONNECTION_COUNT)); + + String ALERT_REPEAT_GAP = "alertRepeatGap"; + String ALERT_THRESHOLD_MESSAGE_AGE = "alertThresholdMessageAge"; + String ALERT_THRESHOLD_MESSAGE_SIZE = "alertThresholdMessageSize"; + String ALERT_THRESHOLD_QUEUE_DEPTH_BYTES = "alertThresholdQueueDepthBytes"; + String ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES = "alertThresholdQueueDepthMessages"; + String DEAD_LETTER_QUEUE_ENABLED = "deadLetterQueueEnabled"; + String FEDERATION_TAG = "federationTag"; + String HOUSEKEEPING_CHECK_PERIOD = "housekeepingCheckPeriod"; + String MAXIMUM_DELIVERY_ATTEMPTS = "maximumDeliveryAttempts"; + String QUEUE_FLOW_CONTROL_SIZE_BYTES = "queueFlowControlSizeBytes"; + String QUEUE_FLOW_RESUME_SIZE_BYTES = "queueFlowResumeSizeBytes"; + String STORE_CONFIGURATION = "storeConfiguration"; + String STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE = "storeTransactionIdleTimeoutClose"; + String STORE_TRANSACTION_IDLE_TIMEOUT_WARN = "storeTransactionIdleTimeoutWarn"; + String STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE = "storeTransactionOpenTimeoutClose"; + String STORE_TRANSACTION_OPEN_TIMEOUT_WARN = "storeTransactionOpenTimeoutWarn"; + String STORE_TYPE = "storeType"; + String SUPPORTED_EXCHANGE_TYPES = "supportedExchangeTypes"; + String SUPPORTED_QUEUE_TYPES = "supportedQueueTypes"; + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + SUPPORTED_EXCHANGE_TYPES, + SUPPORTED_QUEUE_TYPES, + DEAD_LETTER_QUEUE_ENABLED, + FEDERATION_TAG, + HOUSEKEEPING_CHECK_PERIOD, + MAXIMUM_DELIVERY_ATTEMPTS, + QUEUE_FLOW_CONTROL_SIZE_BYTES, + QUEUE_FLOW_RESUME_SIZE_BYTES, + STORE_TYPE, + STORE_CONFIGURATION, + STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, + STORE_TRANSACTION_IDLE_TIMEOUT_WARN, + STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, + STORE_TRANSACTION_OPEN_TIMEOUT_WARN, + ALERT_REPEAT_GAP, + ALERT_THRESHOLD_MESSAGE_AGE, + ALERT_THRESHOLD_MESSAGE_SIZE, + ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, + ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES)); + + + + String getReplicationGroupName(); + + //children + Collection getAliases(); + Collection getConnections(); + Collection getQueues(); + Collection getExchanges(); + + Exchange createExchange(String name, State initialState, boolean durable, + LifetimePolicy lifetime, long ttl, String type, Map attributes) + throws AccessControlException, IllegalArgumentException; + + Queue createQueue(String name, State initialState, boolean durable, + boolean exclusive, LifetimePolicy lifetime, long ttl, Map attributes) + throws AccessControlException, IllegalArgumentException; + + void deleteQueue(Queue queue) throws AccessControlException, IllegalStateException; + + Collection getExchangeTypes(); + + public static interface Transaction + { + void dequeue(QueueEntry entry); + + void copy(QueueEntry entry, Queue queue); + + void move(QueueEntry entry, Queue queue); + + } + + public static interface TransactionalOperation + { + void withinTransaction(Transaction txn); + } + + void executeTransaction(TransactionalOperation op); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHostAlias.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHostAlias.java new file mode 100644 index 0000000000..31403d78e5 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/VirtualHostAlias.java @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model; + +import java.security.AccessControlException; +import java.util.Collection; + +public interface VirtualHostAlias extends ConfiguredObject +{ + // parents + Port getPort(); + VirtualHost getVirtualHost(); + + // children + Collection getAuthenticationMethods(); + + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java new file mode 100644 index 0000000000..78880c232d --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java @@ -0,0 +1,293 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.apache.qpid.server.model.ConfigurationChangeListener; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.IllegalStateTransitionException; +import org.apache.qpid.server.model.State; + +abstract class AbstractAdapter implements ConfiguredObject +{ + private final Map _attributes = new HashMap(); + private final Map, ConfiguredObject> _parents = + new HashMap, ConfiguredObject>(); + private final Collection _changeListeners = + new ArrayList(); + + private final UUID _id; + + protected AbstractAdapter(String... names) + { + StringBuilder sb = new StringBuilder(); + for(String name : names) + { + sb.append('/').append(name); + } + _id = UUID.nameUUIDFromBytes(sb.toString().getBytes()); + } + + protected AbstractAdapter() + { + _id = UUID.randomUUID(); + } + + static String getStringAttribute(String name, Map attributes, String defaultVal) + { + final Object value = attributes.get(name); + return value == null ? defaultVal : String.valueOf(value); + } + + static Map getMapAttribute(String name, Map attributes, Map defaultVal) + { + final Object value = attributes.get(name); + if(value == null) + { + return defaultVal; + } + else if(value instanceof Map) + { + return (Map) value; + } + else + { + throw new IllegalArgumentException("Value for attribute " + name + " is not of required type Map"); + } + } + + + static E getEnumAttribute(Class clazz, String name, Map attributes, E defaultVal) + { + Object obj = attributes.get(name); + if(obj == null) + { + return defaultVal; + } + else if(clazz.isInstance(obj)) + { + return (E) obj; + } + else if(obj instanceof String) + { + return (E) Enum.valueOf(clazz, (String)obj); + } + else + { + throw new IllegalArgumentException("Value for attribute " + name + " is not of required type " + clazz.getSimpleName()); + } + } + + static Boolean getBooleanAttribute(String name, Map attributes, Boolean defaultValue) + { + Object obj = attributes.get(name); + if(obj == null) + { + return defaultValue; + } + else if(obj instanceof Boolean) + { + return (Boolean) obj; + } + else if(obj instanceof String) + { + return Boolean.parseBoolean((String) obj); + } + else + { + throw new IllegalArgumentException("Value for attribute " + name + " is not of required type Boolean"); + } + } + + static Integer getIntegerAttribute(String name, Map attributes, Integer defaultValue) + { + Object obj = attributes.get(name); + if(obj == null) + { + return defaultValue; + } + else if(obj instanceof Number) + { + return ((Number) obj).intValue(); + } + else if(obj instanceof String) + { + return Integer.valueOf((String) obj); + } + else + { + throw new IllegalArgumentException("Value for attribute " + name + " is not of required type Integer"); + } + } + + static Long getLongAttribute(String name, Map attributes, Long defaultValue) + { + Object obj = attributes.get(name); + if(obj == null) + { + return defaultValue; + } + else if(obj instanceof Number) + { + return ((Number) obj).longValue(); + } + else if(obj instanceof String) + { + return Long.valueOf((String) obj); + } + else + { + throw new IllegalArgumentException("Value for attribute " + name + " is not of required type Long"); + } + } + + public final UUID getId() + { + return _id; + } + + public State getDesiredState() + { + return null; //TODO + } + + public State setDesiredState(final State currentState, final State desiredState) + throws IllegalStateTransitionException, AccessControlException + { + return null; //TODO + } + + public void addChangeListener(final ConfigurationChangeListener listener) + { + if(listener == null) + { + throw new NullPointerException("Cannot add a null listener"); + } + synchronized (this) + { + if(!_changeListeners.contains(listener)) + { + _changeListeners.add(listener); + } + } + } + + public boolean removeChangeListener(final ConfigurationChangeListener listener) + { + if(listener == null) + { + throw new NullPointerException("Cannot remove a null listener"); + } + synchronized (this) + { + return _changeListeners.remove(listener); + } + } + + + protected void childAdded(ConfiguredObject child) + { + synchronized (this) + { + for(ConfigurationChangeListener listener : _changeListeners) + { + listener.childAdded(this, child); + } + } + } + + + protected void childRemoved(ConfiguredObject child) + { + synchronized (this) + { + for(ConfigurationChangeListener listener : _changeListeners) + { + listener.childRemoved(this, child); + } + } + } + + public Object getAttribute(final String name) + { + synchronized (this) + { + return _attributes.get(name); + } + } + + public Object setAttribute(final String name, final Object expected, final Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + synchronized (this) + { + Object currentValue = _attributes.get(name); + if((currentValue == null && expected == null) + || (currentValue != null && currentValue.equals(expected))) + { + _attributes.put(name, desired); + return desired; + } + else + { + return currentValue; + } + } + } + + public T getParent(final Class clazz) + { + synchronized (this) + { + return (T) _parents.get(clazz); + } + } + + protected void addParent(Class clazz, T parent) + { + synchronized (this) + { + _parents.put(clazz, parent); + } + } + + protected void removeParent(Class clazz) + { + synchronized (this) + { + _parents.remove(clazz); + } + } + + public Collection getAttributeNames() + { + synchronized(_attributes) + { + return new ArrayList(_attributes.keySet()); + } + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java new file mode 100644 index 0000000000..8a75818f04 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java @@ -0,0 +1,487 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.io.IOException; +import java.security.AccessControlException; +import java.security.Principal; +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.security.auth.login.AccountNotFoundException; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.model.*; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; + +public abstract class AuthenticationProviderAdapter extends AbstractAdapter implements AuthenticationProvider +{ + private static final Logger LOGGER = Logger.getLogger(AuthenticationProviderAdapter.class); + + private final BrokerAdapter _broker; + private final T _authManager; + + private AuthenticationProviderAdapter(BrokerAdapter brokerAdapter, + final T authManager) + { + _broker = brokerAdapter; + _authManager = authManager; + } + + public static AuthenticationProviderAdapter createAuthenticationProviderAdapter(BrokerAdapter brokerAdapter, + final AuthenticationManager authManager) + { + return authManager instanceof PrincipalDatabaseAuthenticationManager + ? new PrincipalDatabaseAuthenticationManagerAdapter(brokerAdapter, (PrincipalDatabaseAuthenticationManager) authManager) + : new SimpleAuthenticationProviderAdapter(brokerAdapter, authManager); + } + + T getAuthManager() + { + return _authManager; + } + + @Override + public Collection getVirtualHostPortBindings() + { + return Collections.emptyList(); + } + + @Override + public String getName() + { + return _authManager.getClass().getSimpleName(); + } + + @Override + public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException + { + return null; + } + + @Override + public State getActualState() + { + return null; + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; + } + + @Override + public long getTimeToLive() + { + return 0; + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getAttributeNames() + { + return AuthenticationProvider.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if(TYPE.equals(name)) + { + return _authManager.getClass().getSimpleName(); + } + else if(CREATED.equals(name)) + { + // TODO + } + else if(DURABLE.equals(name)) + { + return true; + } + else if(ID.equals(name)) + { + return getId(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return LifetimePolicy.PERMANENT; + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return State.ACTIVE; // TODO + } + else if(TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if(UPDATED.equals(name)) + { + // TODO + } + return super.getAttribute(name); + } + + @Override + public Collection getChildren(Class clazz) + { + return null; + } + + @Override + public C createChild(Class childClass, + Map attributes, + ConfiguredObject... otherParents) + { + return null; + } + + private static class SimpleAuthenticationProviderAdapter extends AuthenticationProviderAdapter + { + public SimpleAuthenticationProviderAdapter( + BrokerAdapter brokerAdapter, AuthenticationManager authManager) + { + super(brokerAdapter,authManager); + } + } + + private static class PrincipalDatabaseAuthenticationManagerAdapter + extends AuthenticationProviderAdapter + implements PasswordCredentialManagingAuthenticationProvider + { + public PrincipalDatabaseAuthenticationManagerAdapter( + BrokerAdapter brokerAdapter, PrincipalDatabaseAuthenticationManager authManager) + { + super(brokerAdapter, authManager); + } + + @Override + public boolean createUser(String username, String password, Map attributes) + { + return getPrincipalDatabase().createPrincipal(new UsernamePrincipal(username), password.toCharArray()); + } + + @Override + public void deleteUser(String username) throws AccountNotFoundException + { + if(getSecurityManager().authoriseMethod(Operation.DELETE, + "UserManagement", + "deleteUser")) + { + + getPrincipalDatabase().deletePrincipal(new UsernamePrincipal(username)); + } + else + { + throw new AccessControlException("Cannot delete user " + username); + } + } + + private org.apache.qpid.server.security.SecurityManager getSecurityManager() + { + return ApplicationRegistry.getInstance().getSecurityManager(); + } + + private PrincipalDatabase getPrincipalDatabase() + { + return getAuthManager().getPrincipalDatabase(); + } + + @Override + public void setPassword(String username, String password) throws AccountNotFoundException + { + getPrincipalDatabase().updatePassword(new UsernamePrincipal(username), password.toCharArray()); + } + + public void reload() throws IOException + { + if(getSecurityManager().authoriseMethod(Operation.UPDATE, "UserManagement", "reload")) + { + getPrincipalDatabase().reload(); + } + else + { + throw new AccessControlException("Do not have permission to reload principal database"); + } + } + + @Override + public Map> getUsers() + { + + Map> users = new HashMap>(); + for(Principal principal : getPrincipalDatabase().getUsers()) + { + users.put(principal.getName(), Collections.EMPTY_MAP); + } + return users; + } + + @Override + public C createChild(Class childClass, + Map attributes, + ConfiguredObject... otherParents) + { + if(childClass == User.class) + { + Principal p = new UsernamePrincipal((String) attributes.get("name")); + if(getSecurityManager().authoriseMethod(Operation.UPDATE, "UserManagement", "createUser")) + { + if(getPrincipalDatabase().createPrincipal(p, ((String)attributes.get("password")).toCharArray())) + { + return (C) new PrincipalAdapter(p); + } + } + else + { + throw new AccessControlException("Do not have permission to create a new user"); + } + + } + + return super.createChild(childClass, attributes, otherParents); + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == User.class) + { + List users = getPrincipalDatabase().getUsers(); + Collection principals = new ArrayList(users.size()); + for(Principal user : users) + { + principals.add(new PrincipalAdapter(user)); + } + return (Collection) Collections.unmodifiableCollection(principals); + } + else + { + return super.getChildren(clazz); + } + } + + private class PrincipalAdapter extends AbstractAdapter implements User + { + private final Principal _user; + + + public PrincipalAdapter(Principal user) + { + super(PrincipalDatabaseAuthenticationManagerAdapter.this.getName(), user.getName()); + _user = user; + + } + + @Override + public String getPassword() + { + return null; + } + + @Override + public void setPassword(String password) + { + try + { + PrincipalDatabaseAuthenticationManagerAdapter.this.setPassword(_user.getName(), password); + } + catch (AccountNotFoundException e) + { + throw new IllegalStateException(e); + } + } + + @Override + public String getName() + { + return _user.getName(); + } + + @Override + public String setName(String currentName, String desiredName) + throws IllegalStateException, AccessControlException + { + throw new IllegalStateException("Names cannot be updated"); + } + + @Override + public State getActualState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException("Durability cannot be updated"); + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException("LifetimePolicy cannot be updated"); + } + + @Override + public long getTimeToLive() + { + return 0; + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException("ttl cannot be updated"); + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getChildren(Class clazz) + { + return null; + } + + @Override + public C createChild(Class childClass, + Map attributes, + ConfiguredObject... otherParents) + { + return null; + } + + @Override + public Collection getAttributeNames() + { + return User.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + return super.getAttribute(name); + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if(name.equals(PASSWORD)) + { + setPassword((String)desired); + } + return super.setAttribute(name, + expected, + desired); + } + + @Override + public State setDesiredState(State currentState, State desiredState) + throws IllegalStateTransitionException, AccessControlException + { + if(desiredState == State.DELETED) + { + try + { + deleteUser(_user.getName()); + } + catch (AccountNotFoundException e) + { + LOGGER.warn("Failed to delete user " + _user, e); + } + return State.DELETED; + } + return super.setDesiredState(currentState, desiredState); + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java new file mode 100644 index 0000000000..d3d04be70f --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BindingAdapter.java @@ -0,0 +1,222 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.AMQInternalException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; + +final class BindingAdapter extends AbstractAdapter implements Binding +{ + private final org.apache.qpid.server.binding.Binding _binding; + private Statistics _statistics = NoStatistics.getInstance(); + private final ExchangeAdapter _exchange; + private QueueAdapter _queue; + + public BindingAdapter(final org.apache.qpid.server.binding.Binding binding, + ExchangeAdapter exchangeAdapter, + QueueAdapter queueAdapter) + { + super(exchangeAdapter.getExchange().getVirtualHost().getName(), + exchangeAdapter.getName(), + queueAdapter.getName(), + binding.getBindingKey()); + _binding = binding; + _exchange = exchangeAdapter; + _queue = queueAdapter; + addParent(Queue.class, queueAdapter); + addParent(Exchange.class, exchangeAdapter); + } + + + public ExchangeAdapter getExchange() + { + return _exchange; + } + + public QueueAdapter getQueue() + { + return _queue; + } + + public String getName() + { + return _binding.getBindingKey(); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return _binding.getQueue().isDurable() && _binding.getExchange().isDurable(); + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + return Collections.emptySet(); + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new IllegalArgumentException("Cannot add children to a binding"); + } + + public Map getArguments() + { + return new HashMap (_binding.getArguments()); + } + + public void delete() + { + try + { + _queue.getAMQQueue().getVirtualHost().getBindingFactory().removeBinding(_binding); + } + catch(AMQSecurityException e) + { + throw new AccessControlException(e.getMessage()); + } + catch(AMQInternalException e) + { + throw new IllegalStateException(e); + } + } + + @Override + public Object getAttribute(final String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + + } + else if(DURABLE.equals(name)) + { + return _queue.isDurable() && _exchange.isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return _queue.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE || _exchange.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT; + } + else if(TIME_TO_LIVE.equals(name)) + { + + } + else if(CREATED.equals(name)) + { + + } + else if(UPDATED.equals(name)) + { + + } + else if(EXCHANGE.equals(name)) + { + return _exchange.getName(); + } + else if(QUEUE.equals(name)) + { + return _queue.getName(); + } + else if(ARGUMENTS.equals(name)) + { + return getArguments(); + } + + return super.getAttribute(name); //TODO + } + + @Override + public Object setAttribute(final String name, final Object expected, final Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO + } + + @Override + public Collection getAttributeNames() + { + return Binding.AVAILABLE_ATTRIBUTES; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java new file mode 100644 index 0000000000..ad88dbc613 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java @@ -0,0 +1,484 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.net.InetSocketAddress; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.IAuthenticationManagerRegistry; +import org.apache.qpid.server.transport.QpidAcceptor; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHostRegistry.RegistryChangeListener, + IApplicationRegistry.PortBindingListener, + IAuthenticationManagerRegistry.RegistryChangeListener +{ + + + private final IApplicationRegistry _applicationRegistry; + private String _name; + private final Map _vhostAdapters = + new HashMap(); + private final StatisticsAdapter _statistics; + private final Map _portAdapters = new HashMap(); + private HTTPPortAdapter _httpManagementPort; + private final Map _authManagerAdapters = + new HashMap(); + + + public BrokerAdapter(final IApplicationRegistry instance) + { + _applicationRegistry = instance; + _name = "Broker"; + _statistics = new StatisticsAdapter(instance); + + instance.getVirtualHostRegistry().addRegistryChangeListener(this); + populateVhosts(); + instance.addPortBindingListener(this); + populatePorts(); + instance.addRegistryChangeListener(this); + populateAuthenticationManagers(); + } + + private void populateVhosts() + { + synchronized(_vhostAdapters) + { + Collection actualVhosts = + _applicationRegistry.getVirtualHostRegistry().getVirtualHosts(); + for(org.apache.qpid.server.virtualhost.VirtualHost vh : actualVhosts) + { + if(!_vhostAdapters.containsKey(vh)) + { + _vhostAdapters.put(vh, new VirtualHostAdapter(this, vh)); + } + } + + } + } + + + public Collection getVirtualHosts() + { + synchronized(_vhostAdapters) + { + return new ArrayList(_vhostAdapters.values()); + } + + } + private void populatePorts() + { + synchronized (_portAdapters) + { + Map acceptors = _applicationRegistry.getAcceptors(); + + for(Map.Entry entry : acceptors.entrySet()) + { + if(!_portAdapters.containsKey(entry.getValue())) + { + _portAdapters.put(entry.getValue(), new PortAdapter(this, entry.getValue(), entry.getKey())); + } + } + if(_applicationRegistry.useHTTPManagement()) + { + _httpManagementPort = new HTTPPortAdapter(this, _applicationRegistry.getHTTPManagementPort()); + } + + } + } + + public Collection getPorts() + { + synchronized (_portAdapters) + { + final ArrayList ports = new ArrayList(_portAdapters.values()); + if(_httpManagementPort != null) + { + ports.add(_httpManagementPort); + } + return ports; + } + } + + private void populateAuthenticationManagers() + { + synchronized (_authManagerAdapters) + { + IAuthenticationManagerRegistry authenticationManagerRegistry = + _applicationRegistry.getAuthenticationManagerRegistry(); + if(authenticationManagerRegistry != null) + { + Map authenticationManagers = + authenticationManagerRegistry.getAvailableAuthenticationManagers(); + + for(Map.Entry entry : authenticationManagers.entrySet()) + { + if(!_authManagerAdapters.containsKey(entry.getValue())) + { + _authManagerAdapters.put(entry.getValue(), + AuthenticationProviderAdapter.createAuthenticationProviderAdapter(this, + entry.getValue())); + } + } + } + } + } + + public Collection getAuthenticationProviders() + { + synchronized (_authManagerAdapters) + { + final ArrayList authManagers = + new ArrayList(_authManagerAdapters.values()); + return authManagers; + } + + } + + public VirtualHost createVirtualHost(final String name, + final State initialState, + final boolean durable, + final LifetimePolicy lifetime, + final long ttl, + final Map attributes) + throws AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public VirtualHost createVirtualHost(final Map attributes) + throws AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public void deleteVirtualHost(final VirtualHost vhost) + throws AccessControlException, IllegalStateException + { + //TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + public String getName() + { + return _name; + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + + public State getActualState() + { + return null; //TODO + } + + + public boolean isDurable() + { + return true; + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public long getTimeToLive() + { + return 0; + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == VirtualHost.class) + { + return (Collection) getVirtualHosts(); + } + else if(clazz == Port.class) + { + return (Collection) getPorts(); + } + else if(clazz == AuthenticationProvider.class) + { + return (Collection) getAuthenticationProviders(); + } + + return Collections.emptySet(); + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + if(childClass == VirtualHost.class) + { + return (C) createVirtualHost(attributes); + } + else if(childClass == Port.class) + { + return (C) createPort(attributes); + } + else if(childClass == AuthenticationProvider.class) + { + return (C) createAuthenticationProvider(attributes); + } + else + { + throw new IllegalArgumentException("Cannot create child of class " + childClass.getSimpleName()); + } + } + + private Port createPort(Map attributes) + { + // TODO + return null; + } + + private AuthenticationProvider createAuthenticationProvider(Map attributes) + { + // TODO + return null; + } + + + public void virtualHostRegistered(org.apache.qpid.server.virtualhost.VirtualHost virtualHost) + { + VirtualHostAdapter adapter = null; + synchronized (_vhostAdapters) + { + if(!_vhostAdapters.containsKey(virtualHost)) + { + adapter = new VirtualHostAdapter(this, virtualHost); + _vhostAdapters.put(virtualHost, adapter); + } + } + if(adapter != null) + { + childAdded(adapter); + } + } + + public void virtualHostUnregistered(org.apache.qpid.server.virtualhost.VirtualHost virtualHost) + { + VirtualHostAdapter adapter = null; + + synchronized (_vhostAdapters) + { + adapter = _vhostAdapters.remove(virtualHost); + } + if(adapter != null) + { + childRemoved(adapter); + } + } + + @Override + public void authenticationManagerRegistered(AuthenticationManager authenticationManager) + { + AuthenticationProviderAdapter adapter = null; + synchronized (_authManagerAdapters) + { + if(!_authManagerAdapters.containsKey(authenticationManager)) + { + adapter = + AuthenticationProviderAdapter.createAuthenticationProviderAdapter(this, authenticationManager); + _authManagerAdapters.put(authenticationManager, adapter); + } + } + if(adapter != null) + { + childAdded(adapter); + } + } + + @Override + public void authenticationManagerUnregistered(AuthenticationManager authenticationManager) + { + AuthenticationProviderAdapter adapter; + synchronized (_authManagerAdapters) + { + adapter = _authManagerAdapters.remove(authenticationManager); + } + if(adapter != null) + { + childRemoved(adapter); + } + } + + + @Override + public void bound(QpidAcceptor acceptor, InetSocketAddress bindAddress) + { + synchronized (_portAdapters) + { + if(!_portAdapters.containsKey(acceptor)) + { + PortAdapter adapter = new PortAdapter(this, acceptor, bindAddress); + _portAdapters.put(acceptor, adapter); + childAdded(adapter); + } + } + } + + @Override + public void unbound(QpidAcceptor acceptor) + { + PortAdapter adapter = null; + + synchronized (_portAdapters) + { + adapter = _portAdapters.remove(acceptor); + } + if(adapter != null) + { + childRemoved(adapter); + } + } + + @Override + public Collection getAttributeNames() + { + return AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return State.ACTIVE; + } + else if(DURABLE.equals(name)) + { + return isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return LifetimePolicy.PERMANENT; + } + else if(TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if(CREATED.equals(name)) + { + // TODO + } + else if(UPDATED.equals(name)) + { + // TODO + } + else if(BUILD_VERSION.equals(name)) + { + return QpidProperties.getBuildVersion(); + } + else if(BYTES_RETAINED.equals(name)) + { + // TODO + } + else if(OPERATING_SYSTEM.equals(name)) + { + return System.getProperty("os.name") + " " + + System.getProperty("os.version") + " " + + System.getProperty("os.arch"); + } + else if(PLATFORM.equals(name)) + { + return System.getProperty("java.vendor") + " " + + System.getProperty("java.runtime.version", System.getProperty("java.version")); + } + else if(PROCESS_PID.equals(name)) + { + // TODO + } + else if(PRODUCT_VERSION.equals(name)) + { + return QpidProperties.getReleaseVersion(); + } + else if(STATISTICS_ENABLED.equals(name)) + { + // TODO + } + else if(SUPPORTED_STORE_TYPES.equals(name)) + { + // TODO + } + + return super.getAttribute(name); //TODO - Implement. + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement. + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConnectionAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConnectionAdapter.java new file mode 100644 index 0000000000..8114156e3a --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConnectionAdapter.java @@ -0,0 +1,315 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Session; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.stats.StatisticsGatherer; + +final class ConnectionAdapter extends AbstractAdapter implements Connection +{ + + + + + private AMQConnectionModel _connection; + + private final Map _sessionAdapters = + new HashMap(); + private final Statistics _statistics; + + public ConnectionAdapter(final AMQConnectionModel conn) + { + _connection = conn; + _statistics = new ConnectionStatisticsAdapter(conn); + } + + public Collection getSessions() + { + List actualSessions = _connection.getSessionModels(); + + synchronized (_sessionAdapters) + { + for(AMQSessionModel session : _sessionAdapters.keySet()) + { + if(!actualSessions.contains(session)) + { + _sessionAdapters.remove(session); + } + } + for(AMQSessionModel session : actualSessions) + { + if(!_sessionAdapters.containsKey(session)) + { + _sessionAdapters.put(session, new SessionAdapter(session)); + } + } + return new ArrayList(_sessionAdapters.values()); + } + } + + public void delete() + { + try + { + _connection.close(AMQConstant.CONNECTION_FORCED, "Connection closed by external action"); + } + catch(AMQException e) + { + throw new IllegalStateException(e); + } + } + + public String getName() + { + final String remoteAddressString = _connection.getRemoteAddressString(); + return remoteAddressString.replaceAll("/",""); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return false; //TODO + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return null; //TODO + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + @Override + public Object getAttribute(String name) + { + + if(name.equals(ID)) + { + return getId(); + } + else if (name.equals(NAME)) + { + return getName(); + } + else if(name.equals(CLIENT_ID)) + { + return _connection.getClientId(); + } + else if(name.equals(CLIENT_VERSION)) + { + return _connection.getClientVersion(); + } + else if(name.equals(INCOMING)) + { + + } + else if(name.equals(LOCAL_ADDRESS)) + { + + } + else if(name.equals(PRINCIPAL)) + { + return _connection.getPrincipalAsString(); + } + else if(name.equals(PROPERTIES)) + { + + } + else if(name.equals(REMOTE_ADDRESS)) + { + return _connection.getRemoteAddressString(); + } + else if(name.equals(REMOTE_PROCESS_NAME)) + { + + } + else if(name.equals(REMOTE_PROCESS_PID)) + { + + } + else if(name.equals(SESSION_COUNT_LIMIT)) + { + return _connection.getSessionCountLimit(); + } + return super.getAttribute(name); + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if(name.equals(CLIENT_ID)) + { + + } + else if(name.equals(CLIENT_VERSION)) + { + + } + else if(name.equals(INCOMING)) + { + + } + else if(name.equals(LOCAL_ADDRESS)) + { + + } + else if(name.equals(PRINCIPAL)) + { + + } + else if(name.equals(PROPERTIES)) + { + + } + else if(name.equals(REMOTE_ADDRESS)) + { + + } + else if(name.equals(REMOTE_PROCESS_NAME)) + { + + } + else if(name.equals(REMOTE_PROCESS_PID)) + { + + } + else if(name.equals(SESSION_COUNT_LIMIT)) + { + + } + return super.setAttribute(name, expected, desired); + } + + @Override + public Collection getAttributeNames() + { + final HashSet attrNames = new HashSet(super.getAttributeNames()); + attrNames.addAll(Connection.AVAILABLE_ATTRIBUTES); + return Collections.unmodifiableCollection(attrNames); + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Session.class) + { + return (Collection) getSessions(); + } + else + { + return Collections.emptySet(); + } + } + + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + if(childClass == Session.class) + { + throw new IllegalStateException(); + } + else + { + throw new IllegalArgumentException("Cannot create a child of class " + childClass.getSimpleName()); + } + + } + + private class ConnectionStatisticsAdapter extends StatisticsAdapter + { + public ConnectionStatisticsAdapter(StatisticsGatherer applicationRegistry) + { + super(applicationRegistry); + } + + @Override + public Collection getStatisticNames() + { + return Connection.AVAILABLE_STATISTICS; + } + + @Override + public Object getStatistic(String name) + { + if(LAST_IO_TIME.equals(name)) + { + return _connection.getLastIoTime(); + } + else if(SESSION_COUNT.equals(name)) + { + return _connection.getSessionModels().size(); + } + return super.getStatistic(name); + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java new file mode 100644 index 0000000000..6fb6f4971d --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.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.model.adapter; + +import java.util.Map; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Consumer; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.subscription.Subscription; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Collections; + +public class ConsumerAdapter extends AbstractAdapter implements Consumer +{ + private final Subscription _subscription; + private final QueueAdapter _queue; + private final ConsumerStatistics _statistics; + + public ConsumerAdapter(final QueueAdapter queueAdapter, final Subscription subscription) + { + super(queueAdapter.getVirtualHost().getName(), + queueAdapter.getName(), + subscription.getSessionModel().getConnectionModel().getRemoteAddressString(), + String.valueOf(subscription.getSessionModel().getChannelId()), + subscription.getConsumerName() ); + + _subscription = subscription; + _queue = queueAdapter; + _statistics = new ConsumerStatistics(); + //TODO + } + + public String getName() + { + return _subscription.getConsumerName(); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return false; //TODO + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return null; //TODO + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + @Override + public Collection getAttributeNames() + { + return Consumer.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object setAttribute(final String name, final Object expected, final Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO + } + + @Override + public Object getAttribute(final String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + + } + else if(DURABLE.equals(name)) + { + return false; + } + else if(LIFETIME_POLICY.equals(name)) + { + return LifetimePolicy.AUTO_DELETE; + } + else if(TIME_TO_LIVE.equals(name)) + { + + } + else if(CREATED.equals(name)) + { + + } + else if(UPDATED.equals(name)) + { + + } + else if(DISTRIBUTION_MODE.equals(name)) + { + return _subscription.acquires() ? "MOVE" : "COPY"; + } + else if(SETTLEMENT_MODE.equals(name)) + { + + } + else if(EXCLUSIVE.equals(name)) + { + + } + else if(NO_LOCAL.equals(name)) + { + + } + else if(SELECTOR.equals(name)) + { + + } + return super.getAttribute(name); //TODO + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + return Collections.emptySet(); + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new IllegalArgumentException(); + } + + private class ConsumerStatistics implements Statistics + { + + public Collection getStatisticNames() + { + return AVAILABLE_STATISTICS; + } + + public Object getStatistic(String name) + { + if(name.equals(BYTES_OUT)) + { + return _subscription.getBytesOut(); + } + else if(name.equals(MESSAGES_OUT)) + { + return _subscription.getMessagesOut(); + } + else if(name.equals(STATE_CHANGED)) + { + + } + else if(name.equals(UNACKNOWLEDGED_BYTES)) + { + return _subscription.getUnacknowledgedBytes(); + } + else if(name.equals(UNACKNOWLEDGED_MESSAGES)) + { + return _subscription.getUnacknowledgedMessages(); + } + return null; // TODO - Implement + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java new file mode 100644 index 0000000000..dd22804355 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/ExchangeAdapter.java @@ -0,0 +1,409 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInternalException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Publisher; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +final class ExchangeAdapter extends AbstractAdapter implements Exchange, org.apache.qpid.server.exchange.Exchange.BindingListener +{ + + private final org.apache.qpid.server.exchange.Exchange _exchange; + private final Map _bindingAdapters = + new HashMap(); + private VirtualHostAdapter _vhost; + private final ExchangeStatistics _statistics; + + + public ExchangeAdapter(final VirtualHostAdapter virtualHostAdapter, + final org.apache.qpid.server.exchange.Exchange exchange) + { + super(virtualHostAdapter.getName(), exchange.getName()); + _statistics = new ExchangeStatistics(); + _vhost = virtualHostAdapter; + _exchange = exchange; + addParent(org.apache.qpid.server.model.VirtualHost.class, virtualHostAdapter); + + exchange.addBindingListener(this); + populateBindings(); + } + + private void populateBindings() + { + Collection actualBindings = _exchange.getBindings(); + synchronized (_bindingAdapters) + { + for(Binding binding : actualBindings) + { + if(!_bindingAdapters.containsKey(binding)) + { + QueueAdapter queueAdapter = _vhost.getQueueAdapter(binding.getQueue()); + BindingAdapter adapter = new BindingAdapter(binding, this, queueAdapter); + _bindingAdapters.put(binding, adapter); + + queueAdapter.bindingRegistered(binding, adapter); + } + } + } + + } + + public String getExchangeType() + { + return _exchange.getType().getName().toString(); + } + + public Collection getBindings() + { + synchronized (_bindingAdapters) + { + return new ArrayList(_bindingAdapters.values()); + } + + } + + public Collection getPublishers() + { + // TODO + return Collections.emptyList(); + } + + + public org.apache.qpid.server.model.Binding createBinding(Queue queue, + Map attributes) + throws AccessControlException, IllegalStateException + { + attributes = new HashMap(attributes); + String bindingKey = getStringAttribute(org.apache.qpid.server.model.Binding.NAME, attributes, ""); + Map bindingArgs = getMapAttribute(org.apache.qpid.server.model.Binding.ARGUMENTS, attributes, Collections.EMPTY_MAP); + + attributes.remove(org.apache.qpid.server.model.Binding.NAME); + attributes.remove(org.apache.qpid.server.model.Binding.ARGUMENTS); + + return createBinding(bindingKey, queue, bindingArgs, attributes); + + } + + public org.apache.qpid.server.model.Binding createBinding(String bindingKey, Queue queue, + Map bindingArguments, + Map attributes) + throws AccessControlException, IllegalStateException + { + VirtualHost virtualHost = _vhost.getVirtualHost(); + + + AMQQueue amqQueue = ((QueueAdapter)queue).getAMQQueue(); + + try + { + if(!virtualHost.getBindingFactory().addBinding(bindingKey, amqQueue, _exchange, bindingArguments)) + { + Binding oldBinding = virtualHost.getBindingFactory().getBinding(bindingKey, amqQueue, _exchange, + bindingArguments); + + Map oldArgs = oldBinding.getArguments(); + if((oldArgs == null && !bindingArguments.isEmpty()) || (oldArgs != null && !oldArgs.equals(bindingArguments))) + { + //TODO: generate deterministic UUID + virtualHost.getBindingFactory().replaceBinding(UUIDGenerator.generateUUID(), bindingKey, amqQueue, _exchange, bindingArguments); + } + } + Binding binding = virtualHost.getBindingFactory().getBinding(bindingKey, amqQueue, _exchange, bindingArguments); + + synchronized (_bindingAdapters) + { + return binding == null ? null : _bindingAdapters.get(binding); + } + } + catch(AMQSecurityException e) + { + throw new AccessControlException(e.toString()); + } + catch(AMQInternalException e) + { + throw new IllegalStateException(e); + } + } + + public void delete() + { + try + { + _vhost.getVirtualHost().getExchangeRegistry().unregisterExchange(getName(), false); + } + catch(AMQException e) + { + throw new IllegalStateException(e); + } + } + + public String getName() + { + return _exchange.getName(); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return _exchange.isDurable(); + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return _exchange.isAutoDelete() ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT; + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == org.apache.qpid.server.model.Binding.class) + { + return (Collection) getBindings(); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + if(childClass == org.apache.qpid.server.model.Binding.class) + { + if(otherParents != null && otherParents.length == 1 && otherParents[0] instanceof Queue) + { + Queue queue = (Queue) otherParents[0]; + if(queue.getParent(org.apache.qpid.server.model.VirtualHost.class) == getParent(org.apache.qpid.server.model.VirtualHost.class)) + { + return (C) createBinding(queue, attributes); + } + else + { + throw new IllegalArgumentException("Queue and Exchange parents of a binding must be on same virtual host"); + } + } + else + { + throw new IllegalArgumentException("Other parent must be a queue"); + } + } + else + { + throw new IllegalArgumentException(); + } + } + + public void bindingAdded(org.apache.qpid.server.exchange.Exchange exchange, Binding binding) + { + BindingAdapter adapter = null; + synchronized (_bindingAdapters) + { + if(!_bindingAdapters.containsKey(binding)) + { + QueueAdapter queueAdapter = _vhost.getQueueAdapter(binding.getQueue()); + adapter = new BindingAdapter(binding, this, queueAdapter); + _bindingAdapters.put(binding,adapter); + queueAdapter.bindingRegistered(binding,adapter); + } + } + if(adapter != null) + { + childAdded(adapter); + } + } + + public void bindingRemoved(org.apache.qpid.server.exchange.Exchange exchange, Binding binding) + { + BindingAdapter adapter = null; + synchronized (_bindingAdapters) + { + adapter = _bindingAdapters.remove(binding); + } + if(adapter != null) + { + _vhost.getQueueAdapter(binding.getQueue()).bindingUnregistered(binding); + childRemoved(adapter); + } + } + + org.apache.qpid.server.exchange.Exchange getExchange() + { + return _exchange; + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return State.ACTIVE; + } + else if(DURABLE.equals(name)) + { + return isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return _exchange.isAutoDelete() ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT; + } + else if(TIME_TO_LIVE.equals(name)) + { + + } + else if(CREATED.equals(name)) + { + + } + else if(UPDATED.equals(name)) + { + + } + else if(ALTERNATE_EXCHANGE.equals(name)) + { + return _exchange.getAlternateExchange(); + } + else if(TYPE.equals(name)) + { + return _exchange.getType().getName().asString(); + } + return super.getAttribute(name); + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement + } + + @Override + public Collection getAttributeNames() + { + return AVAILABLE_ATTRIBUTES; + } + + private class ExchangeStatistics implements Statistics + { + + public Collection getStatisticNames() + { + return AVAILABLE_STATISTICS; + } + + public Object getStatistic(String name) + { + if(BINDING_COUNT.equals(name)) + { + return _exchange.getBindingCount(); + } + else if(BYTES_DROPPED.equals(name)) + { + return _exchange.getByteDrops(); + } + else if(BYTES_IN.equals(name)) + { + return _exchange.getByteReceives(); + } + else if(MESSAGES_DROPPED.equals(name)) + { + return _exchange.getMsgDrops(); + } + else if(MESSAGES_IN.equals(name)) + { + return _exchange.getMsgReceives(); + } + else if(PRODUCER_COUNT.equals(name)) + { + + } + else if(STATE_CHANGED.equals(name)) + { + + } + return null; // TODO - Implement + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/HTTPPortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/HTTPPortAdapter.java new file mode 100644 index 0000000000..fdcc5e0184 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/HTTPPortAdapter.java @@ -0,0 +1,262 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.VirtualHostAlias; + +public class HTTPPortAdapter extends AbstractAdapter implements Port +{ + private final BrokerAdapter _broker; + private int _port; + public HTTPPortAdapter(BrokerAdapter brokerAdapter, int port) + { + _broker = brokerAdapter; + _port = port; + + } + + @Override + public String getBindingAddress() + { + return "0.0.0.0"; + } + + @Override + public int getPort() + { + return _port; + } + + @Override + public Collection getTransports() + { + return Collections.singleton(Transport.TCP); + } + + @Override + public void addTransport(Transport transport) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Transport removeTransport(Transport transport) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Collection getProtocols() + { + return Collections.singleton(Protocol.HTTP); + } + + @Override + public void addProtocol(Protocol protocol) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Protocol removeProtocol(Protocol protocol) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Collection getVirtualHostBindings() + { + return Collections.emptySet(); + } + + @Override + public Collection getConnections() + { + return Collections.emptySet(); // TODO - Implement + } + + @Override + public String getName() + { + return getBindingAddress() + ":" + getPort(); // TODO - Implement + } + + @Override + public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public State getActualState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return false; // TODO - Implement + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public long getTimeToLive() + { + return 0; // TODO - Implement + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Connection.class) + { + return (Collection) getConnections(); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new UnsupportedOperationException(); + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return getActualState(); + } + else if(DURABLE.equals(name)) + { + return isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return getLifetimePolicy(); + } + else if(TIME_TO_LIVE.equals(name)) + { + return getTimeToLive(); + } + else if(CREATED.equals(name)) + { + + } + else if(UPDATED.equals(name)) + { + + } + else if(BINDING_ADDRESS.equals(name)) + { + return getBindingAddress(); + } + else if(PORT.equals(name)) + { + return getPort(); + } + else if(PROTOCOLS.equals(name)) + { + return getProtocols(); + } + else if(TRANSPORTS.equals(name)) + { + return getTransports(); + } + + return super.getAttribute(name); //TODO - Implement + } + + @Override + public Collection getAttributeNames() + { + return AVAILABLE_ATTRIBUTES; + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/NoStatistics.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/NoStatistics.java new file mode 100644 index 0000000000..b83887ffe5 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/NoStatistics.java @@ -0,0 +1,46 @@ +package org.apache.qpid.server.model.adapter; + +import org.apache.qpid.server.model.Statistics; + +import java.util.Collection; +import java.util.Collections; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

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

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class NoStatistics implements Statistics +{ + private static final NoStatistics INSTANCE = new NoStatistics(); + + private NoStatistics() + { + } + + public Collection getStatisticNames() + { + return Collections.emptyList(); + } + + public Object getStatistic(String name) + { + return null; + } + + public static NoStatistics getInstance() + { + return INSTANCE; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java new file mode 100644 index 0000000000..0021431ee3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java @@ -0,0 +1,318 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.util.Map; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.VirtualHostAlias; +import org.apache.qpid.server.protocol.AmqpProtocolVersion; +import org.apache.qpid.server.transport.QpidAcceptor; + +import java.net.InetSocketAddress; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class PortAdapter extends AbstractAdapter implements Port +{ + private final BrokerAdapter _broker; + private final QpidAcceptor _acceptor; + private final InetSocketAddress _address; + private final Collection _protocols; + + public PortAdapter(BrokerAdapter brokerAdapter, QpidAcceptor acceptor, InetSocketAddress address) + { + _broker = brokerAdapter; + _acceptor = acceptor; + _address = address; + + List protocols = new ArrayList(); + + for(AmqpProtocolVersion pv : _acceptor.getSupported()) + { + switch(pv) + { + case v0_8: + protocols.add(Protocol.AMQP_0_8); + break; + case v0_9: + protocols.add(Protocol.AMQP_0_9); + break; + case v0_9_1: + protocols.add(Protocol.AMQP_0_9_1); + break; + case v0_10: + protocols.add(Protocol.AMQP_0_10); + break; + case v1_0_0: + protocols.add(Protocol.AMQP_1_0); + break; + } + } + + _protocols = Collections.unmodifiableCollection(protocols); + + } + + @Override + public String getBindingAddress() + { + return _address.getHostName(); + } + + @Override + public int getPort() + { + return _address.getPort(); + } + + @Override + public Collection getTransports() + { + switch (_acceptor.getTransport()) + { + case TCP: + return Collections.singleton(Transport.TCP); + case SSL: + return Collections.singleton(Transport.SSL); + } + + return null; // TODO - Implement + } + + @Override + public void addTransport(Transport transport) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Transport removeTransport(Transport transport) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Collection getProtocols() + { + return _protocols; + } + + @Override + public void addProtocol(Protocol protocol) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Protocol removeProtocol(Protocol protocol) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Collection getVirtualHostBindings() + { + List aliases = new ArrayList(); + for(VirtualHost vh : _broker.getVirtualHosts()) + { + for(VirtualHostAlias alias : vh.getAliases()) + { + if(alias.getPort().equals(this)) + { + aliases.add(alias); + } + } + } + return Collections.unmodifiableCollection(aliases); + } + + @Override + public Collection getConnections() + { + return null; // TODO - Implement + } + + @Override + public String getName() + { + return getBindingAddress() + ":" + getPort(); // TODO - Implement + } + + @Override + public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public State getActualState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return false; // TODO - Implement + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public long getTimeToLive() + { + return 0; // TODO - Implement + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Connection.class) + { + return (Collection) getConnections(); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new UnsupportedOperationException(); + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return getActualState(); + } + else if(DURABLE.equals(name)) + { + return isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return getLifetimePolicy(); + } + else if(TIME_TO_LIVE.equals(name)) + { + return getTimeToLive(); + } + else if(CREATED.equals(name)) + { + + } + else if(UPDATED.equals(name)) + { + + } + else if(BINDING_ADDRESS.equals(name)) + { + return getBindingAddress(); + } + else if(PORT.equals(name)) + { + return getPort(); + } + else if(PROTOCOLS.equals(name)) + { + return getProtocols(); + } + else if(TRANSPORTS.equals(name)) + { + return getTransports(); + } + + return super.getAttribute(name); //TODO - Implement + } + + @Override + public Collection getAttributeNames() + { + return AVAILABLE_ATTRIBUTES; + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java new file mode 100644 index 0000000000..5c35fe5f7b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java @@ -0,0 +1,702 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.ConfiguredObjectFinder; +import org.apache.qpid.server.model.Consumer; +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.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.subscription.Subscription; + +final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.SubscriptionRegistrationListener, AMQQueue.NotificationListener +{ + + static final Map ATTRIBUTE_MAPPINGS = new HashMap(); + static + { + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.ALERT_REPEAT_GAP, "x-qpid-minimum-alert-repeat-gap"); + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.ALERT_THRESHOLD_MESSAGE_AGE, "x-qpid-maximum-message-size"); + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.ALERT_THRESHOLD_MESSAGE_SIZE, "x-qpid-maximum-message-age"); + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, "x-qpid-maximum-message-count"); + + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.MAXIMUM_DELIVERY_ATTEMPTS, "x-qpid-maximum-delivery-count"); + + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.QUEUE_FLOW_CONTROL_SIZE_BYTES, "x-qpid-capacity"); + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.QUEUE_FLOW_RESUME_SIZE_BYTES, "x-qpid-flow-resume-capacity"); + + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.SORT_KEY, "qpid.sort_key"); + QueueAdapter.ATTRIBUTE_MAPPINGS.put(Queue.LVQ_KEY, "qpid.last_value_queue_key"); + + } + + private final AMQQueue _queue; + private final Map _bindingAdapters = + new HashMap(); + private Map _consumerAdapters = + new HashMap(); + + + private final VirtualHostAdapter _vhost; + private QueueStatisticsAdapter _statistics; + private QueueNotificationListener _queueNotificationListener; + + public QueueAdapter(final VirtualHostAdapter virtualHostAdapter, final AMQQueue queue) + { + super(virtualHostAdapter.getName(), queue.getName()); + _vhost = virtualHostAdapter; + addParent(org.apache.qpid.server.model.VirtualHost.class, virtualHostAdapter); + + _queue = queue; + _queue.addSubscriptionRegistrationListener(this); + populateConsumers(); + _statistics = new QueueStatisticsAdapter(queue); + _queue.setNotificationListener(this); + } + + private void populateConsumers() + { + Collection actualSubscriptions = _queue.getConsumers(); + + synchronized (_consumerAdapters) + { + Iterator iter = _consumerAdapters.keySet().iterator(); + for(org.apache.qpid.server.subscription.Subscription subscription : actualSubscriptions) + { + if(!_consumerAdapters.containsKey(subscription)) + { + _consumerAdapters.put(subscription, new ConsumerAdapter(this, subscription)); + } + } + } + } + + public Collection getBindings() + { + synchronized (_bindingAdapters) + { + return new ArrayList(_bindingAdapters.values()); + } + } + + public Collection getConsumers() + { + synchronized (_consumerAdapters) + { + return new ArrayList(_consumerAdapters.values()); + } + + } + + public void visit(final QueueEntryVisitor visitor) + { + _queue.visit(visitor); + } + + public void delete() + { + try + { + _queue.delete(); + if (_queue.isDurable()) + { + + _queue.getVirtualHost().getMessageStore().removeQueue(_queue); + } + } + catch(AMQException e) + { + throw new IllegalStateException(e); + } + } + + public String getName() + { + return _queue.getName(); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return _queue.isDurable(); + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return _queue.isAutoDelete() ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT; + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + + @Override + public Collection getAttributeNames() + { + return Queue.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) throws IllegalStateException, AccessControlException, IllegalArgumentException + { + try + { + if(ALERT_REPEAT_GAP.equals(name)) + { + _queue.setMinimumAlertRepeatGap((Long)desired); + return desired; + } + else if(ALERT_THRESHOLD_MESSAGE_AGE.equals(name)) + { + _queue.setMaximumMessageAge((Long)desired); + return desired; + } + else if(ALERT_THRESHOLD_MESSAGE_SIZE.equals(name)) + { + _queue.setMaximumMessageSize((Long)desired); + return desired; + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_BYTES.equals(name)) + { + _queue.setMaximumQueueDepth((Long)desired); + return desired; + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES.equals(name)) + { + _queue.setMaximumMessageCount((Long)desired); + return desired; + } + else if(ALTERNATE_EXCHANGE.equals(name)) + { + // In future we may want to accept a UUID as an alternative way to identifying the exchange + ExchangeAdapter alternateExchange = (ExchangeAdapter) desired; + _queue.setAlternateExchange(alternateExchange == null ? null : alternateExchange.getExchange()); + return desired; + } + else if(EXCLUSIVE.equals(name)) + { + Boolean exclusiveFlag = (Boolean) desired; + _queue.setExclusive(exclusiveFlag); + return desired; + } + else if(MESSAGE_GROUP_KEY.equals(name)) + { + // TODO + } + else if(MESSAGE_GROUP_DEFAULT_GROUP.equals(name)) + { + // TODO + } + else if(MESSAGE_GROUP_SHARED_GROUPS.equals(name)) + { + // TODO + } + else if(LVQ_KEY.equals(name)) + { + // TODO + } + else if(MAXIMUM_DELIVERY_ATTEMPTS.equals(name)) + { + _queue.setMaximumDeliveryCount((Integer)desired); + return desired; + } + else if(NO_LOCAL.equals(name)) + { + // TODO + } + else if(OWNER.equals(name)) + { + // TODO + } + else if(QUEUE_FLOW_CONTROL_SIZE_BYTES.equals(name)) + { + _queue.setCapacity((Long)desired); + return desired; + } + else if(QUEUE_FLOW_RESUME_SIZE_BYTES.equals(name)) + { + _queue.setFlowResumeCapacity((Long)desired); + return desired; + } + else if(QUEUE_FLOW_STOPPED.equals(name)) + { + // TODO + } + else if(SORT_KEY.equals(name)) + { + // TODO + } + else if(TYPE.equals(name)) + { + // TODO + } + else if (DESCRIPTION.equals(name)) + { + _queue.setDescription((String) desired); + return desired; + } + + return super.setAttribute(name, expected, desired); + } + finally + { + if (_queue.isDurable()) + { + try + { + _queue.getVirtualHost().getMessageStore().updateQueue(_queue); + } + catch (AMQStoreException e) + { + throw new IllegalStateException(e); + } + } + } + } + + @Override + public Object getAttribute(String name) + { + + if(ALERT_REPEAT_GAP.equals(name)) + { + return _queue.getMinimumAlertRepeatGap(); + } + else if(ALERT_THRESHOLD_MESSAGE_AGE.equals(name)) + { + return _queue.getMaximumMessageAge(); + } + else if(ALERT_THRESHOLD_MESSAGE_SIZE.equals(name)) + { + return _queue.getMaximumMessageSize(); + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_BYTES.equals(name)) + { + return _queue.getMaximumQueueDepth(); + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES.equals(name)) + { + return _queue.getMaximumMessageCount(); + } + else if(ALTERNATE_EXCHANGE.equals(name)) + { + org.apache.qpid.server.exchange.Exchange alternateExchange = _queue.getAlternateExchange(); + return alternateExchange == null ? null : + ConfiguredObjectFinder.findConfiguredObjectByName(_vhost.getExchanges(), + alternateExchange.getName()); + } + else if(EXCLUSIVE.equals(name)) + { + return _queue.isExclusive(); + } + else if(MESSAGE_GROUP_KEY.equals(name)) + { + // TODO + } + else if(MESSAGE_GROUP_DEFAULT_GROUP.equals(name)) + { + // TODO + } + else if(MESSAGE_GROUP_SHARED_GROUPS.equals(name)) + { + // TODO + } + else if(LVQ_KEY.equals(name)) + { + if(_queue instanceof ConflationQueue) + { + return ((ConflationQueue)_queue).getConflationKey(); + } + } + else if(MAXIMUM_DELIVERY_ATTEMPTS.equals(name)) + { + return _queue.getMaximumDeliveryCount(); + } + else if(NO_LOCAL.equals(name)) + { + // TODO + } + else if(OWNER.equals(name)) + { + return _queue.getOwner() == null ? null : _queue.getOwner().asString(); + } + else if(QUEUE_FLOW_CONTROL_SIZE_BYTES.equals(name)) + { + return _queue.getCapacity(); + } + else if(QUEUE_FLOW_RESUME_SIZE_BYTES.equals(name)) + { + return _queue.getFlowResumeCapacity(); + } + else if(QUEUE_FLOW_STOPPED.equals(name)) + { + return _queue.isOverfull(); + } + else if(SORT_KEY.equals(name)) + { + if(_queue instanceof SortedQueue) + { + return ((SortedQueue)_queue).getSortedPropertyName(); + } + } + else if(TYPE.equals(name)) + { + if(_queue instanceof SortedQueue) + { + return "sorted"; + } + if(_queue instanceof ConflationQueue) + { + return "lvq"; + } + if(_queue instanceof AMQPriorityQueue) + { + return "priority"; + } + return "standard"; + } + else if(CREATED.equals(name)) + { + // TODO + } + else if(DURABLE.equals(name)) + { + return _queue.isDurable(); + } + else if(ID.equals(name)) + { + return getId(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return _queue.isAutoDelete() ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT; + } + else if(NAME.equals(name)) + { + return _queue.getName(); + } + else if(STATE.equals(name)) + { + return State.ACTIVE; // TODO + } + else if(TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if(UPDATED.equals(name)) + { + // TODO + } + else if (DESCRIPTION.equals(name)) + { + return _queue.getDescription(); + } + + return super.getAttribute(name); + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Consumer.class) + { + return (Collection) getConsumers(); + } + else if(clazz == org.apache.qpid.server.model.Binding.class) + { + return (Collection) getBindings(); + } + else + { + return Collections.emptySet(); + } + } + + public org.apache.qpid.server.model.Binding createBinding(Exchange exchange, Map attributes) + throws AccessControlException, IllegalStateException + { + attributes = new HashMap(attributes); + String bindingKey = getStringAttribute(org.apache.qpid.server.model.Binding.NAME, attributes, ""); + Map bindingArgs = getMapAttribute(org.apache.qpid.server.model.Binding.ARGUMENTS, attributes, Collections.EMPTY_MAP); + + attributes.remove(org.apache.qpid.server.model.Binding.NAME); + attributes.remove(org.apache.qpid.server.model.Binding.ARGUMENTS); + + return exchange.createBinding(bindingKey, this, bindingArgs, attributes); + + } + + + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + if(childClass == org.apache.qpid.server.model.Binding.class) + { + if(otherParents != null && otherParents.length == 1 && otherParents[0] instanceof Exchange) + { + Exchange exchange = (Exchange) otherParents[0]; + if(exchange.getParent(org.apache.qpid.server.model.VirtualHost.class) == getParent(org.apache.qpid.server.model.VirtualHost.class)) + { + return (C) createBinding(exchange, attributes); + } + else + { + throw new IllegalArgumentException("Queue and Exchange parents of a binding must be on same virtual host"); + } + } + else + { + throw new IllegalArgumentException("Other parent must be an exchange"); + } + } + else + { + throw new IllegalArgumentException(); + } + } + + void bindingRegistered(Binding binding, BindingAdapter adapter) + { + synchronized (_bindingAdapters) + { + _bindingAdapters.put(binding, adapter); + } + childAdded(adapter); + } + + void bindingUnregistered(Binding binding) + { + BindingAdapter adapter = null; + synchronized (_bindingAdapters) + { + adapter = _bindingAdapters.remove(binding); + } + if(adapter != null) + { + childRemoved(adapter); + } + } + + AMQQueue getAMQQueue() + { + return _queue; + } + + public void subscriptionRegistered(final AMQQueue queue, final Subscription subscription) + { + ConsumerAdapter adapter = null; + synchronized (_consumerAdapters) + { + if(!_consumerAdapters.containsKey(subscription)) + { + adapter = new ConsumerAdapter(this, subscription); + _consumerAdapters.put(subscription,adapter); + // TODO - register with session + } + } + if(adapter != null) + { + childAdded(adapter); + } + } + + public void subscriptionUnregistered(final AMQQueue queue, final Subscription subscription) + { + ConsumerAdapter adapter = null; + + synchronized (_consumerAdapters) + { + adapter = _consumerAdapters.remove(subscription); + // TODO - register with session + } + if(adapter != null) + { + childRemoved(adapter); + } + } + + VirtualHostAdapter getVirtualHost() + { + return _vhost; + } + + + private static class QueueStatisticsAdapter implements Statistics + { + + private final AMQQueue _queue; + + public QueueStatisticsAdapter(AMQQueue queue) + { + _queue = queue; + } + + public Collection getStatisticNames() + { + return Queue.AVAILABLE_STATISTICS; + } + + public Object getStatistic(String name) + { + if(BINDING_COUNT.equals(name)) + { + return _queue.getBindingCount(); + } + else if(CONSUMER_COUNT.equals(name)) + { + return _queue.getConsumerCount(); + } + else if(CONSUMER_COUNT_WITH_CREDIT.equals(name)) + { + return _queue.getActiveConsumerCount(); + } + else if(DISCARDS_TTL_BYTES.equals(name)) + { + return null; // TODO + } + else if(DISCARDS_TTL_MESSAGES.equals(name)) + { + return null; // TODO + } + else if(PERSISTENT_DEQUEUED_BYTES.equals(name)) + { + return _queue.getPersistentByteDequeues(); + } + else if(PERSISTENT_DEQUEUED_MESSAGES.equals(name)) + { + return _queue.getPersistentMsgDequeues(); + } + else if(PERSISTENT_ENQUEUED_BYTES.equals(name)) + { + return _queue.getPersistentByteEnqueues(); + } + else if(PERSISTENT_ENQUEUED_MESSAGES.equals(name)) + { + return _queue.getPersistentMsgEnqueues(); + } + else if(QUEUE_DEPTH_BYTES.equals(name)) + { + return _queue.getQueueDepth(); + } + else if(QUEUE_DEPTH_MESSAGES.equals(name)) + { + return _queue.getMessageCount(); + } + else if(STATE_CHANGED.equals(name)) + { + return null; // TODO + } + else if(TOTAL_DEQUEUED_BYTES.equals(name)) + { + return _queue.getTotalDequeueSize(); + } + else if(TOTAL_DEQUEUED_MESSAGES.equals(name)) + { + return _queue.getTotalDequeueCount(); + } + else if(TOTAL_ENQUEUED_BYTES.equals(name)) + { + return _queue.getTotalEnqueueSize(); + } + else if(TOTAL_ENQUEUED_MESSAGES.equals(name)) + { + return _queue.getTotalEnqueueCount(); + } + else if(UNACKNOWLEDGED_BYTES.equals(name)) + { + return _queue.getUnackedMessageBytes(); + } + else if(UNACKNOWLEDGED_MESSAGES.equals(name)) + { + return _queue.getUnackedMessageCount(); + } + + return null; + } + } + + @Override + public void setNotificationListener(QueueNotificationListener listener) + { + _queueNotificationListener = listener; + } + + @Override + public void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg) + { + QueueNotificationListener listener = _queueNotificationListener; + if(listener != null) + { + listener.notifyClients(notification, this, notificationMsg); + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java new file mode 100644 index 0000000000..25d9c6feb1 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java @@ -0,0 +1,239 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model.adapter; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +import java.util.Map; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Publisher; +import org.apache.qpid.server.model.Session; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.model.Consumer; +import org.apache.qpid.server.protocol.AMQSessionModel; + +final class SessionAdapter extends AbstractAdapter implements Session +{ + // Attributes + + + private AMQSessionModel _session; + private SessionStatistics _statistics; + + public SessionAdapter(final AMQSessionModel session) + { + _session = session; + _statistics = new SessionStatistics(); + } + + public Collection getSubscriptions() + { + return null; //TODO + } + + public Collection getPublishers() + { + return null; //TODO + } + + public String getName() + { + return String.valueOf(_session.getChannelId()); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + return null; //TODO + } + + public State getActualState() + { + return null; //TODO + } + + public boolean isDurable() + { + return false; //TODO + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + //TODO + } + + public LifetimePolicy getLifetimePolicy() + { + return null; //TODO + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; //TODO + } + + public long getTimeToLive() + { + return 0; //TODO + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; //TODO + } + + @Override + public Collection getAttributeNames() + { + Collection names = new HashSet(super.getAttributeNames()); + names.addAll(AVAILABLE_ATTRIBUTES); + + return Collections.unmodifiableCollection(names); + } + + @Override + public Object getAttribute(String name) + { + if(name.equals(ID)) + { + return getId(); + } + else if (name.equals(NAME)) + { + return getName(); + } + else if(name.equals(CHANNEL_ID)) + { + return _session.getChannelId(); + } + else if(name.equals(PRODUCER_FLOW_BLOCKED)) + { + return _session.getBlocking(); + } + return super.getAttribute(name); //TODO - Implement + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Consumer.class) + { + return (Collection) getSubscriptions(); + } + else if(clazz == Publisher.class) + { + return (Collection) getPublishers(); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new UnsupportedOperationException(); + } + + private class SessionStatistics implements Statistics + { + + public SessionStatistics() + { + } + + public Collection getStatisticNames() + { + return AVAILABLE_STATISTICS; + } + + public Object getStatistic(String name) + { + if(name.equals(BYTES_IN)) + { + } + else if(name.equals(BYTES_OUT)) + { + } + else if(name.equals(CONSUMER_COUNT)) + { + final Collection subscriptions = getSubscriptions(); + return subscriptions == null ? 0 : subscriptions.size(); + } + else if(name.equals(LOCAL_TRANSACTION_BEGINS)) + { + return _session.getTxnStart(); + } + else if(name.equals(LOCAL_TRANSACTION_OPEN)) + { + long open = _session.getTxnCount() - (_session.getTxnCommits() + _session.getTxnRejects()); + return (Boolean) (open > 0l); + } + else if(name.equals(LOCAL_TRANSACTION_ROLLBACKS)) + { + return _session.getTxnCommits(); + } + else if(name.equals(STATE_CHANGED)) + { + } + else if(name.equals(UNACKNOWLEDGED_BYTES)) + { + } + else if(name.equals(UNACKNOWLEDGED_MESSAGES)) + { + return _session.getUnacknowledgedMessageCount(); + } + else if(name.equals(XA_TRANSACTION_BRANCH_ENDS)) + { + } + else if(name.equals(XA_TRANSACTION_BRANCH_STARTS)) + { + } + else if(name.equals(XA_TRANSACTION_BRANCH_SUSPENDS)) + { + + } + + return null; // TODO - Implement + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/StatisticsAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/StatisticsAdapter.java new file mode 100644 index 0000000000..f98e46c911 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/StatisticsAdapter.java @@ -0,0 +1,67 @@ +package org.apache.qpid.server.model.adapter; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.stats.StatisticsGatherer; + +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +*

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

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +class StatisticsAdapter implements Statistics +{ + + private final Map _statistics = + new HashMap(); + + + private static final String BYTES_IN = "bytesIn"; + private static final String BYTES_OUT = "bytesOut"; + private static final String MESSAGES_IN = "messagesIn"; + private static final String MESSAGES_OUT = "messagesOut"; + + private static final Collection STATISTIC_NAMES = + Collections.unmodifiableCollection(Arrays.asList(BYTES_IN, BYTES_OUT, MESSAGES_IN, MESSAGES_OUT)); + + + + public StatisticsAdapter(StatisticsGatherer statGatherer) + { + _statistics.put(BYTES_OUT, statGatherer.getDataDeliveryStatistics()); + _statistics.put(BYTES_IN, statGatherer.getDataReceiptStatistics()); + _statistics.put(MESSAGES_OUT, statGatherer.getMessageDeliveryStatistics()); + _statistics.put(MESSAGES_IN, statGatherer.getMessageReceiptStatistics()); + } + + + public Collection getStatisticNames() + { + return STATISTIC_NAMES; + } + + public Object getStatistic(String name) + { + StatisticsCounter counter = _statistics.get(name); + return counter == null ? null : counter.getTotal(); + + } + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java new file mode 100644 index 0000000000..204054c8cb --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java @@ -0,0 +1,844 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.model.adapter; + +import java.security.AccessControlException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.connection.IConnectionRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.message.ServerMessage; +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.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.Statistics; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.VirtualHostAlias; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.txn.LocalTransaction; +import org.apache.qpid.server.txn.ServerTransaction; + +final class VirtualHostAdapter extends AbstractAdapter implements VirtualHost, ExchangeRegistry.RegistryChangeListener, + QueueRegistry.RegistryChangeListener, + IConnectionRegistry.RegistryChangeListener +{ + + private final org.apache.qpid.server.virtualhost.VirtualHost _virtualHost; + + private final Map _connectionAdapters = + new HashMap(); + + private final Map _queueAdapters = + new HashMap(); + + private final Map _exchangeAdapters = + new HashMap(); + + private final StatisticsAdapter _statistics; + + private final BrokerAdapter _broker; + + private final List _aliases = new ArrayList(); + + + VirtualHostAdapter(BrokerAdapter brokerAdapter, + final org.apache.qpid.server.virtualhost.VirtualHost virtualHost) + { + super(virtualHost.getName()); + _broker = brokerAdapter; + _virtualHost = virtualHost; + _statistics = new VirtualHostStatisticsAdapter(virtualHost); + virtualHost.getQueueRegistry().addRegistryChangeListener(this); + populateQueues(); + virtualHost.getExchangeRegistry().addRegistryChangeListener(this); + populateExchanges(); + virtualHost.getConnectionRegistry().addRegistryChangeListener(this); + populateConnections(); + + + + for(Port port :_broker.getPorts()) + { + _aliases.add(new VirtualHostAliasAdapter(this, port)); + } + } + + + private void populateExchanges() + { + Collection actualExchanges = + _virtualHost.getExchangeRegistry().getExchanges(); + + synchronized (_exchangeAdapters) + { + for(org.apache.qpid.server.exchange.Exchange exchange : actualExchanges) + { + if(!_exchangeAdapters.containsKey(exchange)) + { + _exchangeAdapters.put(exchange, new ExchangeAdapter(this,exchange)); + } + } + } + } + + + private void populateQueues() + { + Collection actualQueues = _virtualHost.getQueueRegistry().getQueues(); + + synchronized(_queueAdapters) + { + for(AMQQueue queue : actualQueues) + { + if(!_queueAdapters.containsKey(queue)) + { + _queueAdapters.put(queue, new QueueAdapter(this,queue)); + } + } + } + } + + private void populateConnections() + { + + List actualConnections = _virtualHost.getConnectionRegistry().getConnections(); + + synchronized(_connectionAdapters) + { + for(AMQConnectionModel conn : actualConnections) + { + if(!_connectionAdapters.containsKey(conn)) + { + _connectionAdapters.put(conn, new ConnectionAdapter(conn)); + } + } + } + + } + + public String getReplicationGroupName() + { + return null; //TODO + } + + public Collection getAliases() + { + return Collections.unmodifiableCollection(_aliases); + } + + public Collection getConnections() + { + synchronized(_connectionAdapters) + { + return new ArrayList(_connectionAdapters.values()); + } + + } + + public Collection getQueues() + { + synchronized(_queueAdapters) + { + return new ArrayList(_queueAdapters.values()); + } + } + + public Collection getExchanges() + { + synchronized (_exchangeAdapters) + { + return new ArrayList(_exchangeAdapters.values()); + } + } + + + public Exchange createExchange(Map attributes) + throws AccessControlException, IllegalArgumentException + { + attributes = new HashMap(attributes); + + String name = getStringAttribute(Exchange.NAME, attributes, null); + State state = getEnumAttribute(State.class, Exchange.STATE, attributes, State.ACTIVE); + boolean durable = getBooleanAttribute(Exchange.DURABLE, attributes, false); + LifetimePolicy lifetime = getEnumAttribute(LifetimePolicy.class, Exchange.LIFETIME_POLICY, attributes, LifetimePolicy.PERMANENT); + String type = getStringAttribute(Exchange.TYPE, attributes, null); + long ttl = getLongAttribute(Exchange.TIME_TO_LIVE, attributes, 0l); + + attributes.remove(Exchange.NAME); + attributes.remove(Exchange.STATE); + attributes.remove(Exchange.DURABLE); + attributes.remove(Exchange.LIFETIME_POLICY); + attributes.remove(Exchange.TYPE); + attributes.remove(Exchange.TIME_TO_LIVE); + + return createExchange(name, state, durable, lifetime, ttl, type, attributes); + } + + + public Exchange createExchange(final String name, + final State initialState, + final boolean durable, + final LifetimePolicy lifetime, + final long ttl, + final String type, + final Map attributes) + throws AccessControlException, IllegalArgumentException + { + try + { + org.apache.qpid.server.exchange.Exchange exchange = + _virtualHost.getExchangeFactory().createExchange(name, type, durable, + lifetime == LifetimePolicy.AUTO_DELETE); + _virtualHost.getExchangeRegistry().registerExchange(exchange); + if(durable) + { + _virtualHost.getMessageStore().createExchange(exchange); + } + + synchronized (_exchangeAdapters) + { + return _exchangeAdapters.get(exchange); + } + } + catch(AMQException e) + { + throw new IllegalArgumentException(e); + } + } + + public Queue createQueue(Map attributes) + throws AccessControlException, IllegalArgumentException + { + attributes = new HashMap(attributes); + + String name = getStringAttribute(Queue.NAME, attributes, null); + State state = getEnumAttribute(State.class, Queue.STATE, attributes, State.ACTIVE); + boolean durable = getBooleanAttribute(Queue.DURABLE, attributes, false); + LifetimePolicy lifetime = getEnumAttribute(LifetimePolicy.class, Queue.LIFETIME_POLICY, attributes, LifetimePolicy.PERMANENT); + long ttl = getLongAttribute(Queue.TIME_TO_LIVE, attributes, 0l); + boolean exclusive= getBooleanAttribute(Queue.EXCLUSIVE, attributes, false); + + attributes.remove(Queue.NAME); + attributes.remove(Queue.STATE); + attributes.remove(Queue.DURABLE); + attributes.remove(Queue.LIFETIME_POLICY); + attributes.remove(Queue.TIME_TO_LIVE); + + List attrNames = new ArrayList(attributes.keySet()); + for(String attr : attrNames) + { + if(QueueAdapter.ATTRIBUTE_MAPPINGS.containsKey(attr)) + { + attributes.put(QueueAdapter.ATTRIBUTE_MAPPINGS.get(attr),attributes.remove(attr)); + } + } + + return createQueue(name, state, durable, exclusive, lifetime, ttl, attributes); + } + + public Queue createQueue(final String name, + final State initialState, + final boolean durable, + boolean exclusive, + final LifetimePolicy lifetime, + final long ttl, + final Map attributes) + throws AccessControlException, IllegalArgumentException + { + String owner = null; + if(exclusive) + { + Set principals = + SecurityManager.getThreadSubject().getPrincipals(); + if(principals != null && !principals.isEmpty()) + { + owner = principals.iterator().next().getName(); + } + } + try + { + if(_virtualHost.getQueueRegistry().getQueue(name)!=null) + { + throw new IllegalArgumentException("Queue with name "+name+" already exists"); + } + AMQQueue queue = + AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(name, _virtualHost.getName()), name, + durable, owner, lifetime == LifetimePolicy.AUTO_DELETE, + exclusive, _virtualHost, attributes); + _virtualHost.getBindingFactory().addBinding(name, queue, _virtualHost.getExchangeRegistry().getDefaultExchange(), null); + + if(durable) + { + _virtualHost.getMessageStore().createQueue(queue, FieldTable.convertToFieldTable(attributes)); + } + synchronized (_queueAdapters) + { + return _queueAdapters.get(queue); + } + + } + catch(AMQException e) + { + throw new IllegalArgumentException(e); + } + + } + + public String getName() + { + return _virtualHost.getName(); + } + + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + throw new IllegalStateException(); + } + + public State getActualState() + { + return getDesiredState(); + } + + public boolean isDurable() + { + return true; + } + + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public long getTimeToLive() + { + return 0; + } + + public long setTimeToLive(final long expected, final long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + public Statistics getStatistics() + { + return _statistics; + } + + @Override + public Collection getChildren(Class clazz) + { + if(clazz == Exchange.class) + { + return (Collection) getExchanges(); + } + else if(clazz == Queue.class) + { + return (Collection) getQueues(); + } + else if(clazz == Connection.class) + { + return (Collection) getConnections(); + } + else if(clazz == VirtualHostAlias.class) + { + return (Collection) getAliases(); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + if(childClass == Exchange.class) + { + return (C) createExchange(attributes); + } + else if(childClass == Queue.class) + { + return (C) createQueue(attributes); + } + else if(childClass == VirtualHostAlias.class) + { + throw new UnsupportedOperationException(); + } + else if(childClass == Connection.class) + { + throw new UnsupportedOperationException(); + } + throw new IllegalArgumentException("Cannot create a child of class " + childClass.getSimpleName()); + } + + public void exchangeRegistered(org.apache.qpid.server.exchange.Exchange exchange) + { + ExchangeAdapter adapter = null; + synchronized (_exchangeAdapters) + { + if(!_exchangeAdapters.containsKey(exchange)) + { + adapter = new ExchangeAdapter(this, exchange); + _exchangeAdapters.put(exchange, adapter); + + } + + } + if(adapter != null) + { + childAdded(adapter); + } + + } + + + public void exchangeUnregistered(org.apache.qpid.server.exchange.Exchange exchange) + { + ExchangeAdapter adapter; + synchronized (_exchangeAdapters) + { + adapter = _exchangeAdapters.remove(exchange); + + } + + if(adapter != null) + { + childRemoved(adapter); + } + } + + public void queueRegistered(AMQQueue queue) + { + QueueAdapter adapter = null; + synchronized (_queueAdapters) + { + if(!_queueAdapters.containsKey(queue)) + { + adapter = new QueueAdapter(this, queue); + _queueAdapters.put(queue, adapter); + + } + + } + if(adapter != null) + { + childAdded(adapter); + } + + } + + public void queueUnregistered(AMQQueue queue) + { + + QueueAdapter adapter; + synchronized (_queueAdapters) + { + adapter = _queueAdapters.remove(queue); + + } + + if(adapter != null) + { + childRemoved(adapter); + } + } + + public void connectionRegistered(AMQConnectionModel connection) + { + ConnectionAdapter adapter = null; + synchronized (_connectionAdapters) + { + if(!_connectionAdapters.containsKey(connection)) + { + adapter = new ConnectionAdapter(connection); + _connectionAdapters.put(connection, adapter); + + } + + } + if(adapter != null) + { + childAdded(adapter); + } + } + + public void connectionUnregistered(AMQConnectionModel connection) + { + + ConnectionAdapter adapter; + synchronized (_connectionAdapters) + { + adapter = _connectionAdapters.remove(connection); + + } + + if(adapter != null) + { + childRemoved(adapter); + } + } + + QueueAdapter getQueueAdapter(AMQQueue queue) + { + synchronized (_queueAdapters) + { + return _queueAdapters.get(queue); + } + } + + public void deleteQueue(Queue queue) + throws AccessControlException, IllegalStateException + { + // TODO + throw new UnsupportedOperationException("Not Yet Implemented"); + } + + public Collection getExchangeTypes() + { + Collection> types = + _virtualHost.getExchangeFactory().getRegisteredTypes(); + + Collection exchangeTypes = new ArrayList(); + + for(ExchangeType type : types) + { + exchangeTypes.add(type.getName().asString()); + } + return Collections.unmodifiableCollection(exchangeTypes); + } + + public void executeTransaction(TransactionalOperation op) + { + MessageStore store = _virtualHost.getMessageStore(); + final LocalTransaction txn = new LocalTransaction(store); + + op.withinTransaction(new Transaction() + { + public void dequeue(final QueueEntry entry) + { + if(entry.acquire()) + { + txn.dequeue(entry.getQueue(), entry.getMessage(), new ServerTransaction.Action() + { + public void postCommit() + { + entry.discard(); + } + + public void onRollback() + { + } + }); + } + } + + public void copy(QueueEntry entry, Queue queue) + { + final ServerMessage message = entry.getMessage(); + final AMQQueue toQueue = ((QueueAdapter)queue).getAMQQueue(); + + txn.enqueue(toQueue, message, new ServerTransaction.Action() + { + public void postCommit() + { + try + { + toQueue.enqueue(message); + } + catch(AMQException e) + { + throw new RuntimeException(e); + } + } + + public void onRollback() + { + } + }); + + } + + public void move(final QueueEntry entry, Queue queue) + { + final ServerMessage message = entry.getMessage(); + final AMQQueue toQueue = ((QueueAdapter)queue).getAMQQueue(); + if(entry.acquire()) + { + txn.enqueue(toQueue, message, + new ServerTransaction.Action() + { + + public void postCommit() + { + try + { + toQueue.enqueue(message); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + } + + public void onRollback() + { + entry.release(); + } + }); + txn.dequeue(entry.getQueue(), message, + new ServerTransaction.Action() + { + + public void postCommit() + { + entry.discard(); + } + + public void onRollback() + { + + } + }); + } + } + + }); + txn.commit(); + } + + org.apache.qpid.server.virtualhost.VirtualHost getVirtualHost() + { + return _virtualHost; + } + + @Override + public Object getAttribute(String name) + { + if(ID.equals(name)) + { + return getId(); + } + else if(NAME.equals(name)) + { + return getName(); + } + else if(STATE.equals(name)) + { + return State.ACTIVE; + } + else if(DURABLE.equals(name)) + { + return isDurable(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return LifetimePolicy.PERMANENT; + } + else if(TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if(CREATED.equals(name)) + { + // TODO + } + else if(UPDATED.equals(name)) + { + // TODO + } + else if(SUPPORTED_EXCHANGE_TYPES.equals(name)) + { + List types = new ArrayList(); + for(ExchangeType type : _virtualHost.getExchangeFactory().getRegisteredTypes()) + { + types.add(type.getName().asString()); + } + return Collections.unmodifiableCollection(types); + } + else if(SUPPORTED_QUEUE_TYPES.equals(name)) + { + // TODO + } + else if(DEAD_LETTER_QUEUE_ENABLED.equals(name)) + { + return _virtualHost.getConfiguration().isDeadLetterQueueEnabled(); + } + else if(FEDERATION_TAG.equals(name)) + { + return _virtualHost.getFederationTag(); + } + else if(HOUSEKEEPING_CHECK_PERIOD.equals(name)) + { + return _virtualHost.getConfiguration().getHousekeepingCheckPeriod(); + } + else if(MAXIMUM_DELIVERY_ATTEMPTS.equals(name)) + { + return _virtualHost.getConfiguration().getMaxDeliveryCount(); + } + else if(QUEUE_FLOW_CONTROL_SIZE_BYTES.equals(name)) + { + return _virtualHost.getConfiguration().getCapacity(); + } + else if(QUEUE_FLOW_RESUME_SIZE_BYTES.equals(name)) + { + return _virtualHost.getConfiguration().getFlowResumeCapacity(); + } + else if(STORE_TYPE.equals(name)) + { + return _virtualHost.getMessageStore().getStoreType(); + } + else if(STORE_CONFIGURATION.equals(name)) + { + // TODO + } + else if(STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE.equals(name)) + { + return _virtualHost.getConfiguration().getTransactionTimeoutIdleClose(); + } + else if(STORE_TRANSACTION_IDLE_TIMEOUT_WARN.equals(name)) + { + return _virtualHost.getConfiguration().getTransactionTimeoutIdleWarn(); + } + else if(STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE.equals(name)) + { + return _virtualHost.getConfiguration().getTransactionTimeoutOpenClose(); + } + else if(STORE_TRANSACTION_OPEN_TIMEOUT_WARN.equals(name)) + { + return _virtualHost.getConfiguration().getTransactionTimeoutOpenWarn(); + } + else if(ALERT_REPEAT_GAP.equals(name)) + { + return _virtualHost.getConfiguration().getMinimumAlertRepeatGap(); + } + else if(ALERT_THRESHOLD_MESSAGE_AGE.equals(name)) + { + return _virtualHost.getConfiguration().getMaximumMessageAge(); + } + else if(ALERT_THRESHOLD_MESSAGE_SIZE.equals(name)) + { + return _virtualHost.getConfiguration().getMaximumMessageSize(); + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_BYTES.equals(name)) + { + return _virtualHost.getConfiguration().getMaximumQueueDepth(); + } + else if(ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES.equals(name)) + { + return _virtualHost.getConfiguration().getMaximumMessageCount(); + } + return super.getAttribute(name); + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return super.setAttribute(name, expected, desired); //TODO - Implement + } + + @Override + public Collection getAttributeNames() + { + return AVAILABLE_ATTRIBUTES; + } + + private static class VirtualHostStatisticsAdapter extends StatisticsAdapter + { + private final org.apache.qpid.server.virtualhost.VirtualHost _vhost; + + private static final Collection VHOST_STATS = Arrays.asList( + VirtualHost.QUEUE_COUNT, + VirtualHost.EXCHANGE_COUNT, + VirtualHost.CONNECTION_COUNT); + + public VirtualHostStatisticsAdapter(org.apache.qpid.server.virtualhost.VirtualHost virtualHost) + { + super(virtualHost); + _vhost = virtualHost; + } + + @Override + public Collection getStatisticNames() + { + Set stats = new HashSet(super.getStatisticNames()); + stats.addAll(VHOST_STATS); + return stats; + } + + @Override + public Object getStatistic(String name) + { + if(VirtualHost.QUEUE_COUNT.equals(name)) + { + return _vhost.getQueueRegistry().getQueues().size(); + } + else if(VirtualHost.EXCHANGE_COUNT.equals(name)) + { + return _vhost.getExchangeRegistry().getExchanges().size(); + } + else if(VirtualHost.CONNECTION_COUNT.equals(name)) + { + return _vhost.getConnectionRegistry().getConnections().size(); + } + else + { + return super.getStatistic(name); + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAliasAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAliasAdapter.java new file mode 100644 index 0000000000..22ea2e612b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAliasAdapter.java @@ -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. + * + */ + +package org.apache.qpid.server.model.adapter; + +import java.util.Map; +import org.apache.qpid.server.model.AuthenticationMethod; +import org.apache.qpid.server.model.ConfiguredObject; +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.Statistics; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.VirtualHostAlias; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Collections; + +public class VirtualHostAliasAdapter extends AbstractAdapter implements VirtualHostAlias +{ + private VirtualHostAdapter _vhost; + private Port _port; + + public VirtualHostAliasAdapter(VirtualHostAdapter virtualHostAdapter, Port port) + { + super(virtualHostAdapter.getName(), port.getName()); + _vhost = virtualHostAdapter; + _port = port; + } + + @Override + public Port getPort() + { + return _port; + } + + @Override + public VirtualHost getVirtualHost() + { + return _vhost; + } + + @Override + public Collection getAuthenticationMethods() + { + return Collections.emptySet(); // TODO - Implement + } + + @Override + public String getName() + { + return _vhost.getName(); + } + + @Override + public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public State getActualState() + { + return State.ACTIVE; // TODO - Implement + } + + @Override + public boolean isDurable() + { + return true; // TODO - Implement + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; // TODO - Implement + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public long getTimeToLive() + { + return 0; // TODO - Implement + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + throw new IllegalStateException(); // TODO - Implement + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getChildren(Class clazz) + { + return Collections.emptySet(); + } + + @Override + public C createChild(Class childClass, Map attributes, ConfiguredObject... otherParents) + { + throw new UnsupportedOperationException(); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties index badeffca05..4677d4b9e6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties @@ -31,12 +31,24 @@ javax.management.openmbean=1.0.0 javax.management=1.0.0 +javax.management.remote.rmi=1.0.0 +javax.management.remote=1.0.0 +javax.management.monitor=1.0.0 + +javax.crypto=1 +javax.crypto.spec=1 + +javax.servlet=2 +javax.servlet.http=2 javax.security.auth=1.0.0 javax.security.auth.callback=1.0.0 +javax.security.auth.login=1.0.0 javax.security.sasl=1.0.0 javax.security=1.0.0 +javax.rmi.ssl=1.0.0 + org.xml.sax=1.0.0 org.xml.sax.helpers=1.0.0 @@ -46,37 +58,57 @@ org.osgi.service.startlevel=1.0.0 org.osgi.service.url=1.0.0 org.osgi.util.tracker=1.0.0 +org.apache.commons.codec=1.3.0 +org.apache.commons.codec.binary=1.3.0 + org.apache.commons.configuration=1.0.0 org.apache.commons.lang=1.0.0 org.apache.commons.lang.builder=1.0.0 +org.apache.commons.lang.time=1.0.0 org.apache.commons.logging=1.0.0 -org.apache.log4j=1.2.12 +org.apache.log4j=1.2.16 org.slf4j=1.6.1 +org.eclipse.jetty=7.6.3 +org.eclipse.jetty.http=7.6.3 +org.eclipse.jetty.io=7.6.3 +org.eclipse.jetty.io.nio=7.6.3 +org.eclipse.jetty.security=7.6.3 +org.eclipse.jetty.server=7.6.3 +org.eclipse.jetty.server.session=7.6.3 +org.eclipse.jetty.servlet=7.6.3 + +org.codehaus.jackson=1.9.0 +org.codehaus.jackson.map=1.9.0 + # For Qpid packages (org.apache.qpid), the version number is automatically overridden by QpidPropertis#getReleaseVersion() -org.apache.qpid.junit.extensions.util=0.0.0 org.apache.qpid=0.0.0 org.apache.qpid.common=0.0.0 org.apache.qpid.exchange=0.0.0 org.apache.qpid.framing=0.0.0 org.apache.qpid.management.common.mbeans.annotations=0.0.0 +org.apache.qpid.management.common.mbeans=0.0.0 org.apache.qpid.protocol=0.0.0 org.apache.qpid.transport=0.0.0 org.apache.qpid.transport.codec=0.0.0 org.apache.qpid.server.binding=0.0.0 +org.apache.qpid.server.model=0.0.0 +org.apache.qpid.server.model.adapter=0.0.0 +org.apache.qpid.server.model.impl=0.0.0 org.apache.qpid.server.configuration=0.0.0 org.apache.qpid.server.configuration.plugins=0.0.0 org.apache.qpid.server.configuration.management=0.0.0 +org.apache.qpid.server.connection=0.0.0 org.apache.qpid.server.exchange=0.0.0 org.apache.qpid.server.logging=0.0.0 org.apache.qpid.server.logging.actors=0.0.0 +org.apache.qpid.server.logging.messages=0.0.0 org.apache.qpid.server.logging.subjects=0.0.0 org.apache.qpid.server.message=0.0.0 -org.apache.qpid.server.management=0.0.0 org.apache.qpid.server.persistent=0.0.0 org.apache.qpid.server.plugins=0.0.0 org.apache.qpid.server.protocol=0.0.0 @@ -88,7 +120,10 @@ org.apache.qpid.server.security.access.plugins=0.0.0 org.apache.qpid.server.security.auth=0.0.0 org.apache.qpid.server.security.auth.sasl=0.0.0 org.apache.qpid.server.security.auth.manager=0.0.0 +org.apache.qpid.server.security.auth.rmi=0.0.0 +org.apache.qpid.server.stats=0.0.0 org.apache.qpid.server.virtualhost=0.0.0 org.apache.qpid.server.virtualhost.plugins=0.0.0 org.apache.qpid.util=0.0.0 +org.apache.qpid.server.store.berkeleydb=0.0.0 diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java index b7fd2387a5..69ba7279bc 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java @@ -77,4 +77,16 @@ public interface AMQConnectionModel extends StatisticsGatherer public String getUserName(); public boolean isSessionNameUnique(byte[] name); + + String getRemoteAddressString(); + + String getClientId(); + + String getClientVersion(); + + String getPrincipalAsString(); + + long getSessionCountLimit(); + + long getLastIoTime(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 5db336649f..850e293c3b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -34,7 +34,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; -import javax.management.JMException; import javax.security.auth.Subject; import javax.security.sasl.SaslServer; import org.apache.log4j.Logger; @@ -62,8 +61,6 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.logging.messages.ConnectionMessages; import org.apache.qpid.server.logging.subjects.ConnectionLogSubject; -import org.apache.qpid.server.management.Managable; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; import org.apache.qpid.server.queue.QueueEntry; @@ -81,7 +78,7 @@ import org.apache.qpid.transport.TransportException; import org.apache.qpid.transport.network.NetworkConnection; import org.apache.qpid.util.BytesDataOutput; -public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQProtocolSession, ConnectionConfig +public class AMQProtocolEngine implements ServerProtocolEngine, AMQProtocolSession, ConnectionConfig { private static final Logger _logger = Logger.getLogger(AMQProtocolEngine.class); @@ -106,8 +103,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private AMQCodecFactory _codecFactory; - private AMQProtocolSessionMBean _managedObject; - private SaslServer _saslServer; private Object _lastReceived; @@ -148,8 +143,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private final ConfigStore _configStore; private long _createTime = System.currentTimeMillis(); - private ApplicationRegistry _registry; - private boolean _statisticsEnabled = false; private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private NetworkConnection _network; @@ -159,11 +152,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private long _lastReceivedTime; private boolean _blocking; - public ManagedObject getManagedObject() - { - return _managedObject; - } - public AMQProtocolEngine(VirtualHostRegistry virtualHostRegistry, NetworkConnection network, final long connectionId) { _stateManager = new AMQStateManager(virtualHostRegistry, this); @@ -181,8 +169,8 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr _actor.message(ConnectionMessages.OPEN(null, null, null, false, false, false)); - _registry = virtualHostRegistry.getApplicationRegistry(); initialiseStatistics(); + } public void setNetworkConnection(NetworkConnection network) @@ -196,11 +184,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr _sender = sender; } - private AMQProtocolSessionMBean createMBean() throws JMException - { - return new AMQProtocolSessionMBean(this); - } - public long getSessionID() { return _connectionID; @@ -649,17 +632,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr { _cachedChannels[channelId] = channel; } - - checkForNotification(); - } - - private void checkForNotification() - { - int channelsCount = _channelMap.size(); - if (_managedObject != null && channelsCount >= _maxNoOfChannels) - { - _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); - } } public Long getMaximumNumberOfChannels() @@ -811,13 +783,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr getConfigStore().removeConfiguredObject(this); - if (_managedObject != null) - { - _managedObject.unregister(); - // Ensure we only do this once. - _managedObject = null; - } - for (Task task : _taskList) { task.doTask(this); @@ -1004,16 +969,6 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr _virtualHost.getConnectionRegistry().registerConnection(this); _configStore.addConfiguredObject(this); - - try - { - _managedObject = createMBean(); - _managedObject.register(); - } - catch (JMException e) - { - _logger.error(e); - } } public void addSessionCloseTask(Task task) @@ -1170,6 +1125,16 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr return _clientVersion; } + public String getPrincipalAsString() + { + return getAuthId(); + } + + public long getSessionCountLimit() + { + return getMaximumNumberOfChannels(); + } + public Boolean isIncoming() { return true; @@ -1400,12 +1365,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public List getSessionModels() { - List sessions = new ArrayList(); - for (AMQChannel channel : getChannels()) - { - sessions.add((AMQSessionModel) channel); - } - return sessions; + return new ArrayList(getChannels()); } public LogSubject getLogSubject() @@ -1415,21 +1375,15 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public void registerMessageDelivered(long messageSize) { - if (isStatisticsEnabled()) - { - _messagesDelivered.registerEvent(1L); - _dataDelivered.registerEvent(messageSize); - } + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); _virtualHost.registerMessageDelivered(messageSize); } public void registerMessageReceived(long messageSize, long timestamp) { - if (isStatisticsEnabled()) - { - _messagesReceived.registerEvent(1L, timestamp); - _dataReceived.registerEvent(messageSize, timestamp); - } + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); _virtualHost.registerMessageReceived(messageSize, timestamp); } @@ -1463,29 +1417,26 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public void initialiseStatistics() { - setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && - _registry.getConfiguration().isStatisticsGenerationConnectionsEnabled()); - _messagesDelivered = new StatisticsCounter("messages-delivered-" + getSessionID()); _dataDelivered = new StatisticsCounter("data-delivered-" + getSessionID()); _messagesReceived = new StatisticsCounter("messages-received-" + getSessionID()); _dataReceived = new StatisticsCounter("data-received-" + getSessionID()); } - public boolean isStatisticsEnabled() + public boolean isSessionNameUnique(byte[] name) { - return _statisticsEnabled; + // 0-8/0-9/0-9-1 sessions don't have names + return true; } - public void setStatisticsEnabled(boolean enabled) + public String getRemoteAddressString() { - _statisticsEnabled = enabled; + return String.valueOf(getRemoteAddress()); } - public boolean isSessionNameUnique(byte[] name) + public String getClientId() { - // 0-8/0-9/0-9-1 sessions don't have names - return true; + return String.valueOf(getContextKey()); } public void setDeferFlush(boolean deferFlush) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java deleted file mode 100644 index e70720600e..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java +++ /dev/null @@ -1,344 +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. - * - */ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License") - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.protocol; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; -import org.apache.qpid.server.management.ManagedObject; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.NotCompliantMBeanException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.Date; -import java.util.List; - -/** - * This MBean class implements the management interface. In order to make more attributes, operations and notifications - * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. - */ -@MBeanDescription("Management Bean for an AMQ Broker 0-9-1/0-9/0-8 Connections") -public class AMQProtocolSessionMBean extends AbstractAMQManagedConnectionObject -{ - private AMQProtocolSession _protocolSession = null; - - private static final AMQShortString BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION = - new AMQShortString(BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR); - - @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-9-1/0-9/0-8 Connection") - public AMQProtocolSessionMBean(AMQProtocolSession amqProtocolSession) throws NotCompliantMBeanException, OpenDataException - { - super(amqProtocolSession.getRemoteAddress().toString()); - _protocolSession = amqProtocolSession; - } - - public String getClientId() - { - return String.valueOf(_protocolSession.getContextKey()); - } - - public String getAuthorizedId() - { - return (_protocolSession.getAuthorizedPrincipal() != null ) ? _protocolSession.getAuthorizedPrincipal().getName() : null; - } - - public String getVersion() - { - return _protocolSession.getClientVersion(); - } - - public Date getLastIoTime() - { - return new Date(_protocolSession.getLastIoTime()); - } - - public String getRemoteAddress() - { - return _protocolSession.getRemoteAddress().toString(); - } - - public ManagedObject getParentObject() - { - return _protocolSession.getVirtualHost().getManagedObject(); - } - - public Long getWrittenBytes() - { - return _protocolSession.getWrittenBytes(); - } - - public Long getReadBytes() - { - return _protocolSession.getWrittenBytes(); - } - - public Long getMaximumNumberOfChannels() - { - return _protocolSession.getMaximumNumberOfChannels(); - } - - /** - * commits transactions for a transactional channel - * - * @param channelId - * @throws JMException if channel with given id doesn't exist or if commit fails - */ - public void commitTransactions(int channelId) throws JMException - { - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - AMQChannel channel = _protocolSession.getChannel(channelId); - if (channel == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - - _protocolSession.commitTransactions(channel); - } - catch (AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - finally - { - CurrentActor.remove(); - } - } - - /** - * rollsback the transactions for a transactional channel - * - * @param channelId - * @throws JMException if channel with given id doesn't exist or if rollback fails - */ - public void rollbackTransactions(int channelId) throws JMException - { - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - AMQChannel channel = _protocolSession.getChannel(channelId); - if (channel == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - - _protocolSession.rollbackTransactions(channel); - } - catch (AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - finally - { - CurrentActor.remove(); - } - } - - /** - * Creates the list of channels in tabular form from the _channelMap. - * - * @return list of channels in tabular form. - * @throws OpenDataException - */ - public TabularData channels() throws OpenDataException - { - TabularDataSupport channelsList = new TabularDataSupport(_channelsType); - List list = _protocolSession.getChannels(); - - for (AMQChannel channel : list) - { - Object[] itemValues = - { - channel.getChannelId(), channel.isTransactional(), - (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getNameShortString().asString() : null, - channel.getUnacknowledgedMessageMap().size(), channel.getBlocking() - }; - - CompositeData channelData = new CompositeDataSupport(_channelType, - COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); - channelsList.put(channelData); - } - - return channelsList; - } - - /** - * closes the connection. The administrator can use this management operation to close connection to free up - * resources. - * @throws JMException - */ - public void closeConnection() throws JMException - { - - MethodRegistry methodRegistry = _protocolSession.getMethodRegistry(); - ConnectionCloseBody responseBody = - methodRegistry.createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), - // replyCode - BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION, - // replyText, - 0, - 0); - - // This seems ugly but because we use closeConnection in both normal - // broker operation and as part of the management interface it cannot - // be avoided. The Current Actor will be null when this method is - // called via the Management interface. This is because we allow the - // Local API connection with JConsole. If we did not allow that option - // then the CurrentActor could be set in our JMX Proxy object. - // As it is we need to set the CurrentActor on all MBean methods - // Ideally we would not have a single method that can be called from - // two contexts. - boolean removeActor = false; - if (CurrentActor.get() == null) - { - removeActor = true; - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - } - - try - { - _protocolSession.writeFrame(responseBody.generateFrame(0)); - - try - { - - _protocolSession.closeSession(); - } - catch (AMQException ex) - { - throw new MBeanException(ex, ex.toString()); - } - } - finally - { - if (removeActor) - { - CurrentActor.remove(); - } - } - } - - public void resetStatistics() throws Exception - { - _protocolSession.resetStatistics(); - } - - public double getPeakMessageDeliveryRate() - { - return _protocolSession.getMessageDeliveryStatistics().getPeak(); - } - - public double getPeakDataDeliveryRate() - { - return _protocolSession.getDataDeliveryStatistics().getPeak(); - } - - public double getMessageDeliveryRate() - { - return _protocolSession.getMessageDeliveryStatistics().getRate(); - } - - public double getDataDeliveryRate() - { - return _protocolSession.getDataDeliveryStatistics().getRate(); - } - - public long getTotalMessagesDelivered() - { - return _protocolSession.getMessageDeliveryStatistics().getTotal(); - } - - public long getTotalDataDelivered() - { - return _protocolSession.getDataDeliveryStatistics().getTotal(); - } - - public double getPeakMessageReceiptRate() - { - return _protocolSession.getMessageReceiptStatistics().getPeak(); - } - - public double getPeakDataReceiptRate() - { - return _protocolSession.getDataReceiptStatistics().getPeak(); - } - - public double getMessageReceiptRate() - { - return _protocolSession.getMessageReceiptStatistics().getRate(); - } - - public double getDataReceiptRate() - { - return _protocolSession.getDataReceiptStatistics().getRate(); - } - - public long getTotalMessagesReceived() - { - return _protocolSession.getMessageReceiptStatistics().getTotal(); - } - - public long getTotalDataReceived() - { - return _protocolSession.getDataReceiptStatistics().getTotal(); - } - - public boolean isStatisticsEnabled() - { - return _protocolSession.isStatisticsEnabled(); - } - - public void setStatisticsEnabled(boolean enabled) - { - _protocolSession.setStatisticsEnabled(enabled); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java index 0896499cda..efc64d9d91 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java @@ -55,7 +55,7 @@ public interface AMQSessionModel extends Comparable * is logged or the connection is closed, depending on the configuration. An open * transaction is one that has recent activity. The transaction age is counted * from the time the transaction was started. An idle transaction is one that - * has had no activity, such as publishing or acknowledgeing messages. + * has had no activity, such as publishing or acknowledging messages. * * @param openWarn time in milliseconds before alerting on open transaction * @param openClose time in milliseconds before closing connection with open transaction @@ -72,6 +72,16 @@ public interface AMQSessionModel extends Comparable void unblock(); + boolean getBlocking(); boolean onSameConnection(InboundMessage inbound); + + int getUnacknowledgedMessageCount(); + + Long getTxnCount(); + Long getTxnStart(); + Long getTxnCommits(); + Long getTxnRejects(); + + int getChannelId(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java index 08926d000d..b4195d7e5a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java @@ -49,6 +49,7 @@ import org.apache.qpid.amqp_1_0.type.transport.Transfer; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.subscription.Subscription; @@ -631,4 +632,46 @@ class Subscription_1_0 implements Subscription { _filters = filters; } + + @Override + public AMQSessionModel getSessionModel() + { + // TODO + return null; + } + + @Override + public long getBytesOut() + { + // TODO + return 0; + } + + @Override + public long getMessagesOut() + { + // TODO + return 0; + } + + @Override + public long getUnacknowledgedBytes() + { + // TODO + return 0; + } + + @Override + public long getUnacknowledgedMessages() + { + // TODO + return 0; + } + + @Override + public String getConsumerName() + { + //TODO + return "TODO"; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index e643338c3d..d3efd63ee0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -28,21 +28,25 @@ import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeReferrer; import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.management.Managable; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -public interface AMQQueue extends Managable, Comparable, ExchangeReferrer, TransactionLogResource, BaseQueue, +public interface AMQQueue extends Comparable, ExchangeReferrer, TransactionLogResource, BaseQueue, QueueConfig { + public interface NotificationListener + { + void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); + } + boolean getDeleteOnNoConsumers(); void setDeleteOnNoConsumers(boolean b); @@ -57,6 +61,12 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer LogSubject getLogSubject(); + long getUnackedMessageBytes(); + + long getTotalDequeueCount(); + + long getTotalEnqueueCount(); + public interface Context { QueueEntry getLastSeenEntry(); @@ -79,6 +89,17 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer void unregisterSubscription(final Subscription subscription) throws AMQException; + Collection getConsumers(); + + interface SubscriptionRegistrationListener + { + void subscriptionRegistered(AMQQueue queue, Subscription subscription); + void subscriptionUnregistered(AMQQueue queue, Subscription subscription); + } + + void addSubscriptionRegistrationListener(SubscriptionRegistrationListener listener); + void removeSubscriptionRegistrationListener(SubscriptionRegistrationListener listener); + int getConsumerCount(); @@ -109,7 +130,7 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer void dequeue(QueueEntry entry, Subscription sub); - void decrementUnackedMsgCount(); + void decrementUnackedMsgCount(QueueEntry queueEntry); boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; @@ -139,20 +160,8 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer */ public List getMessagesRangeOnTheQueue(final long fromPosition, final long toPosition); + void visit(QueueEntryVisitor visitor); - void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); - - void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); - - void removeMessagesFromQueue(long fromMessageId, long toMessageId); - - static interface Visitor - { - boolean visit(QueueEntry entry); - } - - void visit(Visitor visitor); - long getMaximumMessageSize(); @@ -216,8 +225,6 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer void setAlternateExchange(Exchange exchange); - void setAlternateExchange(String exchangeName); - Map getArguments(); void checkCapacity(AMQSessionModel channel); @@ -245,12 +252,12 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer } /** - * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription + * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusive subscription, as a subscription * already exists. * *

*
CRC Card
Responsibilities Collaborations - *
Represent failure to create an exclusize subscription, as a subscription already exists. + *
Represent failure to create an exclusive subscription, as a subscription already exists. *
* * @todo Not an AMQP exception as no status code. @@ -274,9 +281,7 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer ConfigurationPlugin getConfiguration(); - ManagedObject getManagedObject(); - - void setExclusive(boolean exclusive) throws AMQException; + void setExclusive(boolean exclusive); /** * Gets the maximum delivery count. If a message on this queue @@ -295,4 +300,19 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer */ public void setMaximumDeliveryCount(final int maximumDeliveryCount); + void setNotificationListener(NotificationListener listener); + + /** + * Sets the free text description of this queue. + * + * @param description + * + */ + void setDescription(String description); + + /** + * Gets the free text description of this queue. + */ + String getDescription(); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index f2b7d7c56b..d93af2fc25 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.queue; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -41,6 +42,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public class AMQQueueFactory { public static final String X_QPID_PRIORITIES = "x-qpid-priorities"; + public static final String X_QPID_DESCRIPTION = "x-qpid-description"; public static final String QPID_LVQ_KEY = "qpid.LVQ_key"; public static final String QPID_LAST_VALUE_QUEUE = "qpid.last_value_queue"; public static final String QPID_LAST_VALUE_QUEUE_KEY = "qpid.last_value_queue_key"; @@ -350,32 +352,7 @@ public class AMQQueueFactory boolean autodelete = config.getAutoDelete(); boolean exclusive = config.getExclusive(); String owner = config.getOwner(); - Map arguments = null; - - if(config.isLVQ() || config.getLVQKey() != null) - { - arguments = new HashMap(); - arguments.put(QPID_LAST_VALUE_QUEUE, 1); - arguments.put(QPID_LAST_VALUE_QUEUE_KEY, config.getLVQKey() == null ? QPID_LVQ_KEY : config.getLVQKey()); - } - else if (config.getPriority() || config.getPriorities() > 0) - { - arguments = new HashMap(); - arguments.put(X_QPID_PRIORITIES, config.getPriorities() < 0 ? 10 : config.getPriorities()); - } - else if (config.getQueueSortKey() != null && !"".equals(config.getQueueSortKey())) - { - arguments = new HashMap(); - arguments.put(QPID_QUEUE_SORT_KEY, config.getQueueSortKey()); - } - if (!config.getAutoDelete() && config.isDeadLetterQueueEnabled()) - { - if (arguments == null) - { - arguments = new HashMap(); - } - arguments.put(X_QPID_DLQ_ENABLED, true); - } + Map arguments = createQueueArgumentsFromConfig(config); // we need queues that are defined in config to have deterministic ids. UUID id = UUIDGenerator.generateUUID(queueName, host.getName()); @@ -385,7 +362,6 @@ public class AMQQueueFactory return q; } - /** * Validates DLQ and DLE names *

@@ -475,4 +451,43 @@ public class AMQQueueFactory String dlExchangeName = name + serverConfig.getDeadLetterExchangeSuffix(); return dlExchangeName; } + + private static Map createQueueArgumentsFromConfig(QueueConfiguration config) + { + Map arguments = new HashMap(); + + if(config.isLVQ() || config.getLVQKey() != null) + { + arguments.put(QPID_LAST_VALUE_QUEUE, 1); + arguments.put(QPID_LAST_VALUE_QUEUE_KEY, config.getLVQKey() == null ? QPID_LVQ_KEY : config.getLVQKey()); + } + else if (config.getPriority() || config.getPriorities() > 0) + { + arguments.put(X_QPID_PRIORITIES, config.getPriorities() < 0 ? 10 : config.getPriorities()); + } + else if (config.getQueueSortKey() != null && !"".equals(config.getQueueSortKey())) + { + arguments.put(QPID_QUEUE_SORT_KEY, config.getQueueSortKey()); + } + + if (!config.getAutoDelete() && config.isDeadLetterQueueEnabled()) + { + arguments.put(X_QPID_DLQ_ENABLED, true); + } + + if (config.getDescription() != null && !"".equals(config.getDescription())) + { + arguments.put(X_QPID_DESCRIPTION, config.getDescription()); + } + + if (arguments.isEmpty()) + { + return Collections.emptyMap(); + } + else + { + return arguments; + } + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java deleted file mode 100644 index b0d4cb3486..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ /dev/null @@ -1,664 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - -import org.apache.commons.lang.time.FastDateFormat; -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.management.common.mbeans.ManagedQueue; -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.message.AMQMessage; -import org.apache.qpid.server.message.AMQMessageHeader; -import org.apache.qpid.server.message.MessageTransferMessage; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.transport.MessageProperties; - -import javax.management.JMException; -import javax.management.MBeanException; -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 java.util.ArrayList; -import java.util.List; -import java.util.Set; - - -/** - * AMQQueueMBean is the management bean for an {@link AMQQueue}. - * - *

- *
CRC Caption
Responsibilities Collaborations - *
- */ -@MBeanDescription("Management Interface for AMQQueue") -public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, QueueNotificationListener -{ - - /** Used for debugging purposes. */ - private static final Logger LOGGER = Logger.getLogger(AMQQueueMBean.class); - - /** 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); - - private final AMQQueue _queue; - private final String _queueName; - // OpenMBean data types for viewMessages method - - private static OpenType[] _msgAttributeTypes = new OpenType[6]; // AMQ message attribute types. - private static CompositeType _messageDataType = null; // Composite type for representing AMQ Message data. - private static TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list. - - // OpenMBean data types for viewMessageContent method - private static CompositeType _msgContentType = null; - private static OpenType[] _msgContentAttributeTypes = new OpenType[4]; - - private final long[] _lastNotificationTimes = new long[NotificationCheck.values().length]; - private Notification _lastNotification = null; - - - - - @MBeanConstructor("Creates an MBean exposing an AMQQueue") - public AMQQueueMBean(AMQQueue queue) throws JMException - { - super(ManagedQueue.class, ManagedQueue.TYPE); - _queue = queue; - _queueName = queue.getName(); - } - - public ManagedObject getParentObject() - { - return _queue.getVirtualHost().getManagedObject(); - } - - static - { - try - { - init(); - } - catch (JMException ex) - { - // This is not expected to ever occur. - throw new RuntimeException("Got JMException in static initializer.", ex); - } - } - - /** - * initialises the openmbean data types - */ - private static void init() throws OpenDataException - { - _msgContentAttributeTypes[0] = SimpleType.LONG; // For message id - _msgContentAttributeTypes[1] = SimpleType.STRING; // For MimeType - _msgContentAttributeTypes[2] = SimpleType.STRING; // For Encoding - _msgContentAttributeTypes[3] = new ArrayType(1, SimpleType.BYTE); // For message content - _msgContentType = 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()]), - _msgContentAttributeTypes); - - _msgAttributeTypes[0] = SimpleType.LONG; // For message id - _msgAttributeTypes[1] = new ArrayType(1, SimpleType.STRING); // For header attributes - _msgAttributeTypes[2] = SimpleType.LONG; // For size - _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered - _msgAttributeTypes[4] = SimpleType.LONG; // For queue position - _msgAttributeTypes[5] = SimpleType.INTEGER; // For delivery count - - _messageDataType = new CompositeType("Message", "AMQ Message", - VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]), - VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]), _msgAttributeTypes); - _messagelistDataType = new TabularType("Messages", "List of messages", _messageDataType, - VIEW_MSGS_TABULAR_UNIQUE_INDEX.toArray(new String[VIEW_MSGS_TABULAR_UNIQUE_INDEX.size()])); - } - - public String getObjectInstanceName() - { - return ObjectName.quote(_queueName); - } - - public String getName() - { - return _queueName; - } - - public boolean isDurable() - { - return _queue.isDurable(); - } - - public String getOwner() - { - return String.valueOf(_queue.getOwner()); - } - - public boolean isAutoDelete() - { - return _queue.isAutoDelete(); - } - - public Integer getMessageCount() - { - return _queue.getMessageCount(); - } - - public Integer getMaximumDeliveryCount() - { - return _queue.getMaximumDeliveryCount(); - } - - public Long getMaximumMessageSize() - { - return _queue.getMaximumMessageSize(); - } - - public Long getMaximumMessageAge() - { - return _queue.getMaximumMessageAge(); - } - - public void setMaximumMessageAge(Long maximumMessageAge) - { - _queue.setMaximumMessageAge(maximumMessageAge); - } - - public void setMaximumMessageSize(Long value) - { - _queue.setMaximumMessageSize(value); - } - - public Integer getConsumerCount() - { - return _queue.getConsumerCount(); - } - - public Integer getActiveConsumerCount() - { - return _queue.getActiveConsumerCount(); - } - - public Long getReceivedMessageCount() - { - return _queue.getReceivedMessageCount(); - } - - public Long getMaximumMessageCount() - { - return _queue.getMaximumMessageCount(); - } - - public void setMaximumMessageCount(Long value) - { - _queue.setMaximumMessageCount(value); - } - - /** - * returns the maximum total size of messages(bytes) in the queue. - */ - public Long getMaximumQueueDepth() - { - return _queue.getMaximumQueueDepth(); - } - - public void setMaximumQueueDepth(Long value) - { - _queue.setMaximumQueueDepth(value); - } - - /** - * returns the total size of messages(bytes) in the queue. - */ - public Long getQueueDepth() throws JMException - { - return _queue.getQueueDepth(); - } - - public Long getCapacity() - { - return _queue.getCapacity(); - } - - public void setCapacity(Long capacity) throws IllegalArgumentException - { - if( _queue.getFlowResumeCapacity() > capacity ) - { - throw new IllegalArgumentException("Capacity must not be less than FlowResumeCapacity"); - } - - _queue.setCapacity(capacity); - } - - public Long getFlowResumeCapacity() - { - return _queue.getFlowResumeCapacity(); - } - - public void setFlowResumeCapacity(Long flowResumeCapacity) throws IllegalArgumentException - { - if( _queue.getCapacity() < flowResumeCapacity ) - { - throw new IllegalArgumentException("FlowResumeCapacity must not exceed Capacity"); - } - - _queue.setFlowResumeCapacity(flowResumeCapacity); - } - - public boolean isFlowOverfull() - { - return _queue.isOverfull(); - } - - public boolean isExclusive() - { - return _queue.isExclusive(); - } - - public void setExclusive(boolean exclusive) throws JMException - { - try - { - _queue.setExclusive(exclusive); - } - catch (AMQException e) - { - throw new JMException(e.toString()); - } - } - - public void setAlternateExchange(String exchangeName) - { - _queue.setAlternateExchange(exchangeName); - } - - public String getAlternateExchange() - { - Exchange exchange = _queue.getAlternateExchange(); - String name = exchange == null ? null : exchange.getName(); - return name == null ? null : name; - } - - /** - * Checks if there is any notification to be send to the listeners - */ - public void checkForNotification(ServerMessage msg) throws AMQException - { - - final Set notificationChecks = _queue.getNotificationChecks(); - - if(!notificationChecks.isEmpty()) - { - final long currentTime = System.currentTimeMillis(); - final long thresholdTime = currentTime - _queue.getMinimumAlertRepeatGap(); - - for (NotificationCheck check : notificationChecks) - { - if (check.isMessageSpecific() || (_lastNotificationTimes[check.ordinal()] < thresholdTime)) - { - if (check.notifyIfNecessary(msg, _queue, this)) - { - _lastNotificationTimes[check.ordinal()] = currentTime; - } - } - } - } - - } - - /** - * Sends the notification to the listeners - */ - public void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg) - { - // important : add log to the log file - monitoring tools may be looking for this - LOGGER.info(notification.name() + " On Queue " + queue.getNameShortString() + " - " + notificationMsg); - notificationMsg = notification.name() + " " + notificationMsg; - - _lastNotification = - new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, incrementAndGetSequenceNumber(), - System.currentTimeMillis(), notificationMsg); - - getBroadcaster().sendNotification(_lastNotification); - } - - public Notification getLastNotification() - { - return _lastNotification; - } - - /** - * @see AMQQueue#deleteMessageFromTop - */ - public void deleteMessageFromTop() throws JMException - { - _queue.deleteMessageFromTop(); - } - - /** - * Clears the queue of non-acquired messages - * - * @return the number of messages deleted - * @see AMQQueue#clearQueue - */ - public Long clearQueue() throws JMException - { - try - { - return _queue.clearQueue(); - } - catch (AMQException ex) - { - throw new MBeanException(ex, "Error clearing queue " + _queueName); - } - } - - /** - * returns message content as byte array and related attributes for the given message id. - */ - public CompositeData viewMessageContent(long msgId) throws JMException - { - QueueEntry entry = _queue.getMessageOnTheQueue(msgId); - - if (entry == null) - { - throw new OperationsException("AMQMessage with message id = " + msgId + " is not in the " + _queueName); - } - - ServerMessage serverMsg = entry.getMessage(); - final int bodySize = (int) serverMsg.getSize(); - - - List msgContent = new ArrayList(); - - java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(bodySize); - int position = 0; - - while(position < bodySize) - { - position += serverMsg.getContent(buf, position); - buf.flip(); - for(int i = 0; i < buf.limit(); i++) - { - msgContent.add(buf.get(i)); - } - buf.clear(); - } - - AMQMessageHeader header = serverMsg.getMessageHeader(); - - String mimeType = null, encoding = null; - if (header != null) - { - mimeType = header.getMimeType(); - - encoding = header.getEncoding(); - } - - - Object[] itemValues = { msgId, mimeType, encoding, msgContent.toArray(new Byte[0]) }; - - return new CompositeDataSupport(_msgContentType, - VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.toArray( - new String[VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); - - } - - /** - * Returns the header contents of the messages stored in this queue in tabular form. - * Deprecated as of Qpid JMX API 1.3 - */ - @Deprecated - public TabularData viewMessages(int beginIndex, int endIndex) throws JMException - { - return viewMessages((long)beginIndex,(long)endIndex); - } - - - /** - * Returns the header contents of the messages stored in this queue in tabular form. - * @param startPosition The queue position of the first message to be viewed - * @param endPosition The queue position of the last message to be viewed - */ - public TabularData viewMessages(long startPosition, long endPosition) throws 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 list = _queue.getMessagesRangeOnTheQueue(startPosition,endPosition); - TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType); - - try - { - // Create the tabular list of message header contents - int size = list.size(); - - for (int i = 0; i < size ; i++) - { - long position = startPosition + i; - final QueueEntry queueEntry = list.get(i); - ServerMessage serverMsg = queueEntry.getMessage(); - - String[] headerAttributes = null; - Object[] itemValues = null; - - if(serverMsg instanceof AMQMessage) - { - AMQMessage msg = (AMQMessage) serverMsg; - ContentHeaderBody headerBody = msg.getContentHeaderBody(); - // Create header attributes list - headerAttributes = getMessageHeaderProperties(headerBody); - itemValues = new Object[]{msg.getMessageId(), headerAttributes, headerBody.getBodySize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; - } - else if(serverMsg instanceof MessageTransferMessage) - { - // We have a 0-10 message - MessageTransferMessage msg = (MessageTransferMessage) serverMsg; - - // Create header attributes list - headerAttributes = getMessageTransferMessageHeaderProps(msg); - itemValues = new Object[]{msg.getMessageNumber(), headerAttributes, msg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; - } - else - { - //unknown message - headerAttributes = new String[]{"N/A"}; - itemValues = new Object[]{serverMsg.getMessageNumber(), headerAttributes, serverMsg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; - } - - CompositeData messageData = new CompositeDataSupport(_messageDataType, - VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); - _messageList.put(messageData); - } - } - catch (AMQException e) - { - JMException jme = new JMException("Error creating message contents: " + e); - jme.initCause(e); - throw jme; - } - - return _messageList; - } - - private String[] getMessageHeaderProperties(ContentHeaderBody headerBody) - { - List list = new ArrayList(); - BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) headerBody.getProperties(); - list.add("reply-to = " + headerProperties.getReplyToAsString()); - list.add("propertyFlags = " + headerProperties.getPropertyFlags()); - list.add("ApplicationID = " + headerProperties.getAppIdAsString()); - list.add("ClusterID = " + headerProperties.getClusterIdAsString()); - list.add("UserId = " + headerProperties.getUserIdAsString()); - list.add("JMSMessageID = " + headerProperties.getMessageIdAsString()); - list.add("JMSCorrelationID = " + headerProperties.getCorrelationIdAsString()); - - int delMode = headerProperties.getDeliveryMode(); - list.add("JMSDeliveryMode = " + - ((delMode == BasicContentHeaderProperties.PERSISTENT) ? "Persistent" : "Non_Persistent")); - - list.add("JMSPriority = " + headerProperties.getPriority()); - list.add("JMSType = " + headerProperties.getType()); - - final long expirationDate = headerProperties.getExpiration(); - final long timestampDate = headerProperties.getTimestamp(); - - addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, - timestampDate); - - return list.toArray(new String[list.size()]); - } - - private String[] getMessageTransferMessageHeaderProps(MessageTransferMessage msg) - { - List list = new ArrayList(); - - AMQMessageHeader header = msg.getMessageHeader(); - MessageProperties msgProps = msg.getHeader().getMessageProperties(); - - String appID = null; - String userID = null; - - if(msgProps != null) - { - appID = msgProps.getAppId() == null ? "null" : new String(msgProps.getAppId()); - userID = msgProps.getUserId() == null ? "null" : new String(msgProps.getUserId()); - } - - list.add("reply-to = " + header.getReplyTo()); - list.add("propertyFlags = "); //TODO - list.add("ApplicationID = " + appID); - list.add("ClusterID = "); //TODO - list.add("UserId = " + userID); - list.add("JMSMessageID = " + header.getMessageId()); - list.add("JMSCorrelationID = " + header.getCorrelationId()); - list.add("JMSDeliveryMode = " + (msg.isPersistent() ? "Persistent" : "Non_Persistent")); - list.add("JMSPriority = " + header.getPriority()); - list.add("JMSType = " + header.getType()); - - final long expirationDate = header.getExpiration(); - final long timestampDate = header.getTimestamp(); - addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, timestampDate); - - return list.toArray(new String[list.size()]); - } - - private void addStringifiedJMSTimestamoAndJMSExpiration(final List list, - final long expirationDate, final long timestampDate) - { - final String formattedExpirationDate = (expirationDate != 0) ? FAST_DATE_FORMAT.format(expirationDate) : null; - final String formattedTimestampDate = (timestampDate != 0) ? FAST_DATE_FORMAT.format(timestampDate) : null; - list.add("JMSExpiration = " + formattedExpirationDate); - list.add("JMSTimestamp = " + formattedTimestampDate); - } - - /** - * @see ManagedQueue#moveMessages - * @param fromMessageId - * @param toMessageId - * @param toQueueName - * @throws JMException - */ - public void moveMessages(long fromMessageId, long toMessageId, String toQueueName) throws JMException - { - if ((fromMessageId > toMessageId) || (fromMessageId < 1)) - { - throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); - } - - _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); - } - - /** - * @see ManagedQueue#deleteMessages - * @param fromMessageId - * @param toMessageId - * @throws JMException - */ - public void deleteMessages(long fromMessageId, long toMessageId) throws JMException - { - if ((fromMessageId > toMessageId) || (fromMessageId < 1)) - { - throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); - } - - _queue.removeMessagesFromQueue(fromMessageId, toMessageId); - } - - /** - * @see ManagedQueue#copyMessages - * @param fromMessageId - * @param toMessageId - * @param toQueueName - * @throws JMException - */ - public void copyMessages(long fromMessageId, long toMessageId, String toQueueName) throws JMException - { - if ((fromMessageId > toMessageId) || (fromMessageId < 1)) - { - throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); - } - - _queue.copyMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); - } - - /** - * 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 }; - } - -} // End of AMQQueueMBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java index 2493974d45..27a9e13617 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java @@ -20,12 +20,10 @@ */ package org.apache.qpid.server.queue; -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.ArrayList; import java.util.Collection; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -33,11 +31,11 @@ import java.util.concurrent.ConcurrentMap; public class DefaultQueueRegistry implements QueueRegistry { - private static final Logger LOGGER = Logger.getLogger(DefaultExchangeRegistry.class); - private ConcurrentMap _queueMap = new ConcurrentHashMap(); private final VirtualHost _virtualHost; + private final Collection _listeners = + new ArrayList(); public DefaultQueueRegistry(VirtualHost virtualHost) { @@ -52,11 +50,28 @@ public class DefaultQueueRegistry implements QueueRegistry public void registerQueue(AMQQueue queue) { _queueMap.put(queue.getNameShortString(), queue); + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.queueRegistered(queue); + } + } } public void unregisterQueue(AMQShortString name) { - _queueMap.remove(name); + AMQQueue q = _queueMap.remove(name); + if(q != null) + { + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.queueUnregistered(q); + } + } + } } public AMQQueue getQueue(AMQShortString name) @@ -79,19 +94,30 @@ public class DefaultQueueRegistry implements QueueRegistry return getQueue(new AMQShortString(queue)); } + public void addRegistryChangeListener(RegistryChangeListener listener) + { + synchronized(_listeners) + { + _listeners.add(listener); + } + } + @Override public void stopAllAndUnregisterMBeans() { for (final AMQQueue queue : getQueues()) { queue.stop(); - try - { - queue.getManagedObject().unregister(); - } - catch (AMQException e) + + //TODO: this is a bit of a hack, what if the listeners aren't aware + //that we are just unregistering the MBean because of HA, and aren't + //actually removing the queue as such. + synchronized (_listeners) { - LOGGER.warn("Failed to unregister mbean", e); + for(RegistryChangeListener listener : _listeners) + { + listener.queueUnregistered(queue); + } } } _queueMap.clear(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java index c1ebbe412f..3efef9ab98 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.queue; +import org.apache.log4j.Logger; import org.apache.qpid.server.message.ServerMessage; public enum NotificationCheck @@ -27,13 +28,16 @@ public enum NotificationCheck MESSAGE_COUNT_ALERT { - boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, QueueNotificationListener listener) + public boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, AMQQueue.NotificationListener listener) { int msgCount; final long maximumMessageCount = queue.getMaximumMessageCount(); if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount) { - listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."); + String notificationMsg = msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."; + + logNotification(this, queue, notificationMsg); + listener.notifyClients(this, queue, notificationMsg); return true; } return false; @@ -41,7 +45,7 @@ public enum NotificationCheck }, MESSAGE_SIZE_ALERT(true) { - boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, QueueNotificationListener listener) + public boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, AMQQueue.NotificationListener listener) { final long maximumMessageSize = queue.getMaximumMessageSize(); if(maximumMessageSize != 0) @@ -50,10 +54,12 @@ public enum NotificationCheck long messageSize; messageSize = (msg == null) ? 0 : msg.getSize(); - if (messageSize >= maximumMessageSize) { - listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageNumber() + "]"); + String notificationMsg = messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageNumber() + "]"; + + logNotification(this, queue, notificationMsg); + listener.notifyClients(this, queue, notificationMsg); return true; } } @@ -63,7 +69,7 @@ public enum NotificationCheck }, QUEUE_DEPTH_ALERT { - boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, QueueNotificationListener listener) + public boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, AMQQueue.NotificationListener listener) { // Check for threshold queue depth in bytes final long maximumQueueDepth = queue.getMaximumQueueDepth(); @@ -74,7 +80,10 @@ public enum NotificationCheck if (queueDepth >= maximumQueueDepth) { - listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); + String notificationMsg = (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."; + + logNotification(this, queue, notificationMsg); + listener.notifyClients(this, queue, notificationMsg); return true; } } @@ -84,7 +93,7 @@ public enum NotificationCheck }, MESSAGE_AGE_ALERT { - boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, QueueNotificationListener listener) + public boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, AMQQueue.NotificationListener listener) { final long maxMessageAge = queue.getMaximumMessageAge(); @@ -97,7 +106,10 @@ public enum NotificationCheck if(firstArrivalTime < thresholdTime) { long oldestAge = currentTime - firstArrivalTime; - listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); + String notificationMsg = (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."; + + logNotification(this, queue, notificationMsg); + listener.notifyClients(this, queue, notificationMsg); return true; } @@ -109,6 +121,8 @@ public enum NotificationCheck } ; + private static final Logger LOGGER = Logger.getLogger(NotificationCheck.class); + private final boolean _messageSpecific; NotificationCheck() @@ -126,6 +140,11 @@ public enum NotificationCheck return _messageSpecific; } - abstract boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, QueueNotificationListener listener); + public abstract boolean notifyIfNecessary(ServerMessage msg, AMQQueue queue, AMQQueue.NotificationListener listener); + //A bit of a hack, only for use until we do the logging listener + private static void logNotification(NotificationCheck notification, AMQQueue queue, String notificationMsg) + { + LOGGER.info(notification.name() + " On Queue " + queue.getNameShortString() + " - " + notificationMsg); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 209553e8fa..25e771a9cf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -233,7 +233,7 @@ public abstract class QueueEntryImpl implements QueueEntry if(state instanceof SubscriptionAcquiredState) { - getQueue().decrementUnackedMsgCount(); + getQueue().decrementUnackedMsgCount(this); Subscription subscription = ((SubscriptionAcquiredState)state).getSubscription(); if (subscription != null) { @@ -369,7 +369,7 @@ public abstract class QueueEntryImpl implements QueueEntry Subscription s = null; if (state instanceof SubscriptionAcquiredState) { - getQueue().decrementUnackedMsgCount(); + getQueue().decrementUnackedMsgCount(this); s = ((SubscriptionAcquiredState) state).getSubscription(); s.onDequeue(this); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java deleted file mode 100644 index 959ca03c80..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - - -public interface QueueNotificationListener -{ - void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java index 72a54c9889..e8c34128e9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java @@ -42,7 +42,15 @@ public interface QueueRegistry AMQQueue getQueue(String queue); + void addRegistryChangeListener(RegistryChangeListener listener); + void stopAllAndUnregisterMBeans(); AMQQueue getQueue(UUID queueId); + + interface RegistryChangeListener + { + void queueRegistered(AMQQueue queue); + void queueUnregistered(AMQQueue queue); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index d7eb304c92..3d54bba48f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -19,8 +19,10 @@ package org.apache.qpid.server.queue; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -32,8 +34,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import javax.management.JMException; - import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; @@ -52,7 +52,6 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.QueueActor; import org.apache.qpid.server.logging.messages.QueueMessages; import org.apache.qpid.server.logging.subjects.QueueLogSubject; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.registry.ApplicationRegistry; @@ -70,6 +69,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, MessageGroupManager.SubscriptionResetHelper { private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.class); + private static final String QPID_GROUP_HEADER_KEY = "qpid.group_header_key"; private static final String QPID_SHARED_MSG_GROUP = "qpid.shared_msg_group"; private static final String QPID_DEFAULT_MESSAGE_GROUP = "qpid.default-message-group"; @@ -77,11 +77,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // TODO - should make this configurable at the vhost / broker level private static final int DEFAULT_MAX_GROUPS = 255; - private final VirtualHost _virtualHost; private final AMQShortString _name; - private final String _resourceName; /** null means shared */ private final AMQShortString _owner; @@ -118,6 +116,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private final AtomicLong _dequeueCount = new AtomicLong(); private final AtomicLong _dequeueSize = new AtomicLong(); + private final AtomicLong _enqueueCount = new AtomicLong(); private final AtomicLong _enqueueSize = new AtomicLong(); private final AtomicLong _persistentMessageEnqueueSize = new AtomicLong(); private final AtomicLong _persistentMessageDequeueSize = new AtomicLong(); @@ -130,6 +129,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private final AtomicLong _byteTxnDequeues = new AtomicLong(0); private final AtomicLong _unackedMsgCount = new AtomicLong(0); private final AtomicLong _unackedMsgCountHigh = new AtomicLong(0); + private final AtomicLong _unackedMsgBytes = new AtomicLong(); private final AtomicInteger _bindingCountHigh = new AtomicInteger(); @@ -173,7 +173,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private LogSubject _logSubject; private LogActor _logActor; - private AMQQueueMBean _managedObject; private static final String SUB_FLUSH_RUNNER = "SUB_FLUSH_RUNNER"; private boolean _nolocal; @@ -191,6 +190,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private int _maximumDeliveryCount = ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount(); private final MessageGroupManager _messageGroupManager; + private final Collection _subscriptionListeners = + new ArrayList(); + + private AMQQueue.NotificationListener _notificationListener; + private final long[] _lastNotificationTimes = new long[NotificationCheck.values().length]; + protected SimpleAMQQueue(UUID id, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map arguments) { this(id, name, durable, owner, autoDelete, exclusive,virtualHost, new SimpleQueueEntryList.Factory(), arguments); @@ -227,14 +232,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } _name = name; - _resourceName = String.valueOf(name); _durable = durable; _owner = owner; _autoDelete = autoDelete; _exclusive = exclusive; _virtualHost = virtualHost; _entries = entryListFactory.createQueueEntryList(this); - _arguments = arguments; + _arguments = arguments == null ? new HashMap() : new HashMap(arguments); _id = id; @@ -255,16 +259,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes getConfigStore().addConfiguredObject(this); - try - { - _managedObject = new AMQQueueMBean(this); - _managedObject.register(); - } - catch (JMException e) - { - _logger.error("AMQQueue MBean creation has failed ", e); - } - if(arguments != null && arguments.containsKey(QPID_GROUP_HEADER_KEY)) { if(arguments.containsKey(QPID_SHARED_MSG_GROUP) && String.valueOf(arguments.get(QPID_SHARED_MSG_GROUP)).equals("1")) @@ -339,15 +333,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { return _exclusive; } - - public void setExclusive(boolean exclusive) throws AMQException + + public void setExclusive(boolean exclusive) { _exclusive = exclusive; - - if(isDurable()) - { - getVirtualHost().getMessageStore().updateQueue(this); - } } public Exchange getAlternateExchange() @@ -368,22 +357,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes _alternateExchange = exchange; } - public void setAlternateExchange(String exchangeName) - { - if(exchangeName == null || exchangeName.equals("")) - { - _alternateExchange = null; - return; - } - - Exchange exchange = getVirtualHost().getExchangeRegistry().getExchange(new AMQShortString(exchangeName)); - if (exchange == null) - { - throw new RuntimeException("Exchange '" + exchangeName + "' is not registered with the VirtualHost."); - } - setAlternateExchange(exchange); - } - + /** + * Arguments used to create this queue. The caller is assured + * that null will never be returned. + */ public Map getArguments() { return _arguments; @@ -430,8 +407,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { throw new AMQSecurityException("Permission denied"); } - - + + if (hasExclusiveSubscriber()) { throw new ExistingExclusiveSubscription(); @@ -463,15 +440,24 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { subscription.setNoLocal(_nolocal); } + + synchronized (_subscriptionListeners) + { + for(SubscriptionRegistrationListener listener : _subscriptionListeners) + { + listener.subscriptionRegistered(this, subscription); + } + } + _subscriptionList.add(subscription); - + //Increment consumerCountHigh if necessary. (un)registerSubscription are both //synchronized methods so we don't need additional synchronization here if(_counsumerCountHigh.get() < getConsumerCount()) { _counsumerCountHigh.incrementAndGet(); } - + if (isDeleted()) { subscription.queueDeleted(this); @@ -507,6 +493,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes resetSubPointersForGroups(subscription, true); } + synchronized (_subscriptionListeners) + { + for(SubscriptionRegistrationListener listener : _subscriptionListeners) + { + listener.subscriptionUnregistered(this, subscription); + } + } + // auto-delete queues must be deleted if there are no remaining subscribers if (_autoDelete && getDeleteOnNoConsumers() && !subscription.isTransient() && getConsumerCount() == 0 ) @@ -526,6 +520,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } + public Collection getConsumers() + { + List consumers = new ArrayList(); + SubscriptionList.SubscriptionNodeIterator iter = _subscriptionList.iterator(); + while(iter.advance()) + { + consumers.add(iter.getNode().getSubscription()); + } + return consumers; + + } + + public void addSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + { + synchronized (_subscriptionListeners) + { + _subscriptionListeners.add(listener); + } + } + + public void removeSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + { + synchronized (_subscriptionListeners) + { + _subscriptionListeners.remove(listener); + } + } + public void resetSubPointersForGroups(Subscription subscription, boolean clearAssignments) { QueueEntry entry = _messageGroupManager.findEarliestAssignedAvailableEntry(subscription); @@ -576,10 +598,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes break; } } - + reconfigure(); } - + private void reconfigure() { //Reconfigure the queue for to reflect this new binding. @@ -604,7 +626,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void removeBinding(final Binding binding) { _bindings.remove(binding); - + reconfigure(); } @@ -718,10 +740,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - if(_managedObject != null) - { - _managedObject.checkForNotification(entry.getMessage()); - } + checkForNotification(entry.getMessage()); if(action != null) { @@ -738,8 +757,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { try { - if (!sub.isSuspended() - && subscriptionReadyAndHasInterest(sub, entry) + if (!sub.isSuspended() + && subscriptionReadyAndHasInterest(sub, entry) && mightAssign(sub, entry) && !sub.wouldSuspend(entry)) { @@ -788,6 +807,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { long size = message.getSize(); getAtomicQueueSize().addAndGet(size); + _enqueueCount.incrementAndGet(); _enqueueSize.addAndGet(size); if(message.isPersistent() && isDurable()) { @@ -796,19 +816,29 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } + public long getTotalDequeueCount() + { + return _dequeueCount.get(); + } + + public long getTotalEnqueueCount() + { + return _enqueueCount.get(); + } + private void incrementQueueCount() { getAtomicQueueCount().incrementAndGet(); } - + private void incrementTxnEnqueueStats(final ServerMessage message) { _msgTxnEnqueues.incrementAndGet(); _byteTxnEnqueues.addAndGet(message.getSize()); } - + private void incrementTxnDequeueStats(QueueEntry entry) - { + { _msgTxnDequeues.incrementAndGet(); _byteTxnDequeues.addAndGet(entry.getSize()); } @@ -819,7 +849,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes setLastSeenEntry(sub, entry); _deliveredMessages.incrementAndGet(); - incrementUnackedMsgCount(); + incrementUnackedMsgCount(entry); sub.send(entry, batch); } @@ -887,7 +917,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { _deliveredMessages.decrementAndGet(); } - + if(sub != null && sub.isSessionTransactional()) { incrementTxnDequeueStats(entry); @@ -940,11 +970,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } + + public int getConsumerCount() { return _subscriptionList.size(); } - + public int getConsumerCountHigh() { return _counsumerCountHigh.get(); @@ -1148,7 +1180,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - public void visit(final Visitor visitor) + public void visit(final QueueEntryVisitor visitor) { QueueEntryIterator queueListIterator = _entries.iterator(); @@ -1195,192 +1227,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - public void moveMessagesToAnotherQueue(final long fromMessageId, - final long toMessageId, - String destinationQueueName) throws IllegalArgumentException - { - - final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); - - List entries = getMessagesOnTheQueue(new QueueEntryFilter() - { - - public boolean accept(QueueEntry entry) - { - final long messageId = entry.getMessage().getMessageNumber(); - return (messageId >= fromMessageId) - && (messageId <= toMessageId) - && entry.acquire(); - } - - public boolean filterComplete() - { - return false; - } - }); - - - final ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); - boolean shouldRollback = true; - try - { - // Move the messages in on the message store. - for (final QueueEntry entry : entries) - { - final ServerMessage message = entry.getMessage(); - txn.enqueue(toQueue, message, - new ServerTransaction.Action() - { - - public void postCommit() - { - try - { - toQueue.enqueue(message); - } - catch (AMQException e) - { - throw new RuntimeException(e); - } - } - - public void onRollback() - { - entry.release(); - } - }); - txn.dequeue(this, message, - new ServerTransaction.Action() - { - - public void postCommit() - { - entry.discard(); - } - - public void onRollback() - { - - } - }); - } - txn.commit(); - shouldRollback = false; - } - finally - { - if (shouldRollback) - { - txn.rollback(); - } - } - - } - - public void copyMessagesToAnotherQueue(final long fromMessageId, - final long toMessageId, - String destinationQueueName) throws IllegalArgumentException - { - final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); - - List entries = getMessagesOnTheQueue(new QueueEntryFilter() - { - - public boolean accept(QueueEntry entry) - { - final long messageId = entry.getMessage().getMessageNumber(); - return ((messageId >= fromMessageId) - && (messageId <= toMessageId)); - } - - public boolean filterComplete() - { - return false; - } - }); - - final ServerTransaction txn = new LocalTransaction(_virtualHost.getMessageStore()); - boolean shouldRollback = true; - try - { - // Copy the messages in on the message store. - for (QueueEntry entry : entries) - { - final ServerMessage message = entry.getMessage(); - - txn.enqueue(toQueue, message, new ServerTransaction.Action() - { - public void postCommit() - { - try - { - toQueue.enqueue(message); - } - catch (AMQException e) - { - throw new RuntimeException(e); - } - } - - public void onRollback() - { - } - }); - - } - - txn.commit(); - shouldRollback = false; - } - finally - { - if (shouldRollback) - { - txn.rollback(); - } - } - - } - - private AMQQueue getValidatedDestinationQueue(String queueName) - { - final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (toQueue == null) - { - throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); - } - else if (toQueue == this) - { - throw new IllegalArgumentException("The destination queue can't be the same as the source queue"); - } - return toQueue; - } - - public void removeMessagesFromQueue(long fromMessageId, long toMessageId) - { - - QueueEntryIterator queueListIterator = _entries.iterator(); - - while (queueListIterator.advance()) - { - QueueEntry node = queueListIterator.getNode(); - - final ServerMessage message = node.getMessage(); - if(message != null) - { - final long messageId = message.getMessageNumber(); - - if ((messageId >= fromMessageId) - && (messageId <= toMessageId) - && node.acquire()) - { - dequeueEntry(node); - } - } - } - - } - public void purge(final long request) throws AMQException { clear(request); @@ -1393,6 +1239,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // ------ Management functions + // TODO - now only used by the tests public void deleteMessageFromTop() { QueueEntryIterator queueListIterator = _entries.iterator(); @@ -1411,7 +1258,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } public long clearQueue() throws AMQException - { + { return clear(0l); } @@ -1422,7 +1269,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { throw new AMQSecurityException("Permission denied: queue " + getName()); } - + QueueEntryIterator queueListIterator = _entries.iterator(); long count = 0; @@ -1489,7 +1336,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { throw new AMQSecurityException("Permission denied: " + getName()); } - + if (!_deleted.getAndSet(true)) { @@ -1617,12 +1464,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes txn.commit(); - - if(_managedObject!=null) - { - _managedObject.unregister(); - } - for (Task task : _deleteTaskList) { task.doTask(this); @@ -2101,16 +1942,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } else { - if (_managedObject != null) + // There is a chance that the node could be deleted by + // the time the check actually occurs. So verify we + // can actually get the message to perform the check. + ServerMessage msg = node.getMessage(); + if (msg != null) { - // There is a chance that the node could be deleted by - // the time the check actually occurs. So verify we - // can actually get the message to perform the check. - ServerMessage msg = node.getMessage(); - if (msg != null) - { - _managedObject.checkForNotification(msg); - } + checkForNotification(msg); } } } @@ -2235,11 +2073,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _notificationChecks; } - public ManagedObject getManagedObject() - { - return _managedObject; - } - private final class QueueEntryListener implements QueueEntry.StateChangeListener { @@ -2330,12 +2163,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _queueConfiguration; } - public String getResourceName() - { - return _resourceName; - } - - public ConfigStore getConfigStore() { return getVirtualHost().getConfigStore(); @@ -2355,22 +2182,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { return _dequeueSize.get(); } - + public long getByteTxnEnqueues() { return _byteTxnEnqueues.get(); } - + public long getByteTxnDequeues() { return _byteTxnDequeues.get(); } - + public long getMsgTxnEnqueues() { return _msgTxnEnqueues.get(); } - + public long getMsgTxnDequeues() { return _msgTxnDequeues.get(); @@ -2407,21 +2234,28 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { return _unackedMsgCountHigh.get(); } - + public long getUnackedMessageCount() { return _unackedMsgCount.get(); } - - public void decrementUnackedMsgCount() + + public long getUnackedMessageBytes() + { + return _unackedMsgBytes.get(); + } + + public void decrementUnackedMsgCount(QueueEntry queueEntry) { _unackedMsgCount.decrementAndGet(); + _unackedMsgBytes.addAndGet(-queueEntry.getSize()); } - - private void incrementUnackedMsgCount() + + private void incrementUnackedMsgCount(QueueEntry entry) { long unackedMsgCount = _unackedMsgCount.incrementAndGet(); - + _unackedMsgBytes.addAndGet(entry.getSize()); + long unackedMsgCountHigh; while(unackedMsgCount > (unackedMsgCountHigh = _unackedMsgCountHigh.get())) { @@ -2447,4 +2281,54 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes _maximumDeliveryCount = maximumDeliveryCount; } + /** + * Checks if there is any notification to send to the listeners + */ + private void checkForNotification(ServerMessage msg) throws AMQException + { + final Set notificationChecks = getNotificationChecks(); + final AMQQueue.NotificationListener listener = _notificationListener; + + if(listener != null && !notificationChecks.isEmpty()) + { + final long currentTime = System.currentTimeMillis(); + final long thresholdTime = currentTime - getMinimumAlertRepeatGap(); + + for (NotificationCheck check : notificationChecks) + { + if (check.isMessageSpecific() || (_lastNotificationTimes[check.ordinal()] < thresholdTime)) + { + if (check.notifyIfNecessary(msg, this, listener)) + { + _lastNotificationTimes[check.ordinal()] = currentTime; + } + } + } + } + } + + public void setNotificationListener(AMQQueue.NotificationListener listener) + { + _notificationListener = listener; + } + + @Override + public void setDescription(String description) + { + if (description == null) + { + _arguments.remove(AMQQueueFactory.X_QPID_DESCRIPTION); + } + else + { + _arguments.put(AMQQueueFactory.X_QPID_DESCRIPTION, description); + } + } + + @Override + public String getDescription() + { + return (String) _arguments.get(AMQQueueFactory.X_QPID_DESCRIPTION); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 80a91be262..10c69b7f97 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -22,9 +22,9 @@ package org.apache.qpid.server.registry; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; +import org.apache.qpid.server.logging.*; import org.osgi.framework.BundleContext; -import org.apache.qpid.AMQException; import org.apache.qpid.common.Closeable; import org.apache.qpid.common.QpidProperties; import org.apache.qpid.qmf.QMFService; @@ -35,18 +35,13 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.SystemConfig; import org.apache.qpid.server.configuration.SystemConfigImpl; import org.apache.qpid.server.configuration.VirtualHostConfiguration; -import org.apache.qpid.server.logging.CompositeStartupMessageLogger; -import org.apache.qpid.server.logging.Log4jMessageLogger; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.RootMessageLogger; -import org.apache.qpid.server.logging.SystemOutMessageLogger; import org.apache.qpid.server.logging.actors.AbstractActor; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BrokerMessages; import org.apache.qpid.server.logging.messages.VirtualHostMessages; -import org.apache.qpid.server.management.ManagedObjectRegistry; -import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.adapter.BrokerAdapter; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; @@ -72,6 +67,7 @@ import java.util.concurrent.atomic.AtomicReference; */ public abstract class ApplicationRegistry implements IApplicationRegistry { + private static final Logger _logger = Logger.getLogger(ApplicationRegistry.class); private static AtomicReference _instance = new AtomicReference(null); @@ -81,11 +77,9 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private final Map _acceptors = Collections.synchronizedMap(new HashMap()); - private ManagedObjectRegistry _managedObjectRegistry; - private IAuthenticationManagerRegistry _authenticationManagerRegistry; - private VirtualHostRegistry _virtualHostRegistry; + private final VirtualHostRegistry _virtualHostRegistry = new VirtualHostRegistry(this); private SecurityManager _securityManager; @@ -101,30 +95,32 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private QMFService _qmfService; - private BrokerConfig _broker; + private BrokerConfig _brokerConfig; + + private Broker _broker; private ConfigStore _configStore; - + private Timer _reportingTimer; - private boolean _statisticsEnabled = false; private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private BundleContext _bundleContext; + private final List _portBindingListeners = new ArrayList(); - protected Map getAcceptors() - { - return _acceptors; - } + private int _httpManagementPort = -1; - protected void setManagedObjectRegistry(ManagedObjectRegistry managedObjectRegistry) - { - _managedObjectRegistry = managedObjectRegistry; - } + private LogRecorder _logRecorder; + + private List _authManagerChangeListeners = + new ArrayList(); - protected void setVirtualHostRegistry(VirtualHostRegistry virtualHostRegistry) + public Map getAcceptors() { - _virtualHostRegistry = virtualHostRegistry; + synchronized (_acceptors) + { + return new HashMap(_acceptors); + } } protected void setSecurityManager(SecurityManager securityManager) @@ -191,11 +187,11 @@ public abstract class ApplicationRegistry implements IApplicationRegistry store.setRoot(new SystemConfigImpl(store)); instance.setConfigStore(store); - BrokerConfig broker = new BrokerConfigAdapter(instance); + final BrokerConfig brokerConfig = new BrokerConfigAdapter(instance); - SystemConfig system = store.getRoot(); - system.addBroker(broker); - instance.setBroker(broker); + final SystemConfig system = store.getRoot(); + system.addBroker(brokerConfig); + instance.setBrokerConfig(brokerConfig); try { @@ -208,7 +204,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry //remove the Broker instance, then re-throw try { - system.removeBroker(broker); + system.removeBroker(brokerConfig); } catch(Throwable t) { @@ -283,18 +279,28 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public void initialise() throws Exception { + _logRecorder = new LogRecorder(); //Create the RootLogger to be used during broker operation _rootMessageLogger = new Log4jMessageLogger(_configuration); //Create the composite (log4j+SystemOut MessageLogger to be used during startup RootMessageLogger[] messageLoggers = {new SystemOutMessageLogger(), _rootMessageLogger}; _startupMessageLogger = new CompositeStartupMessageLogger(messageLoggers); - - CurrentActor.set(new BrokerActor(_startupMessageLogger)); + + BrokerActor actor = new BrokerActor(_startupMessageLogger); + CurrentActor.setDefault(actor); + CurrentActor.set(actor); try { - initialiseManagedObjectRegistry(); + initialiseStatistics(); + + if(_configuration.getHTTPManagementEnabled()) + { + _httpManagementPort = _configuration.getHTTPManagementPort(); + } + + _broker = new BrokerAdapter(this); configure(); @@ -302,13 +308,23 @@ public abstract class ApplicationRegistry implements IApplicationRegistry logStartupMessages(CurrentActor.get()); - _virtualHostRegistry = new VirtualHostRegistry(this); - _securityManager = new SecurityManager(_configuration, _pluginManager); _authenticationManagerRegistry = createAuthenticationManagerRegistry(_configuration, _pluginManager); - _managedObjectRegistry.start(); + if(!_authManagerChangeListeners.isEmpty()) + { + for(IAuthenticationManagerRegistry.RegistryChangeListener listener : _authManagerChangeListeners) + { + + _authenticationManagerRegistry.addRegistryChangeListener(listener); + for(AuthenticationManager authMgr : _authenticationManagerRegistry.getAvailableAuthenticationManagers().values()) + { + listener.authenticationManagerRegistered(authMgr); + } + } + _authManagerChangeListeners.clear(); + } } finally { @@ -319,7 +335,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { initialiseVirtualHosts(); - initialiseStatistics(); initialiseStatisticsReporting(); } finally @@ -344,23 +359,18 @@ public abstract class ApplicationRegistry implements IApplicationRegistry getVirtualHostRegistry().setDefaultVirtualHostName(_configuration.getDefaultVirtualHost()); } - protected void initialiseManagedObjectRegistry() throws AMQException - { - _managedObjectRegistry = new NoopManagedObjectRegistry(); - } - public void initialiseStatisticsReporting() { long report = _configuration.getStatisticsReportingPeriod() * 1000; // convert to ms final boolean broker = _configuration.isStatisticsGenerationBrokerEnabled(); final boolean virtualhost = _configuration.isStatisticsGenerationVirtualhostsEnabled(); final boolean reset = _configuration.isStatisticsReportResetEnabled(); - + /* add a timer task to report statistics if generation is enabled for broker or virtualhosts */ if (report > 0L && (broker || virtualhost)) { _reportingTimer = new Timer("Statistics-Reporting", true); - + _reportingTimer.scheduleAtFixedRate(new StatisticsReportingTask(broker, virtualhost, reset), @@ -495,9 +505,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry close(_pluginManager); - close(_managedObjectRegistry); - - BrokerConfig broker = getBroker(); + BrokerConfig broker = getBrokerConfig(); if(broker != null) { broker.getSystem().removeBroker(broker); @@ -513,12 +521,14 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private void unbind() { + List removedAcceptors = new ArrayList(); synchronized (_acceptors) { for (InetSocketAddress bindAddress : _acceptors.keySet()) { QpidAcceptor acceptor = _acceptors.get(bindAddress); + removedAcceptors.add(acceptor); try { acceptor.getNetworkTransport().close(); @@ -531,6 +541,16 @@ public abstract class ApplicationRegistry implements IApplicationRegistry CurrentActor.get().message(BrokerMessages.SHUTTING_DOWN(acceptor.toString(), bindAddress.getPort())); } } + synchronized (_portBindingListeners) + { + for(QpidAcceptor acceptor : removedAcceptors) + { + for(PortBindingListener listener : _portBindingListeners) + { + listener.unbound(acceptor); + } + } + } } public ServerConfiguration getConfiguration() @@ -544,6 +564,13 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { _acceptors.put(bindAddress, acceptor); } + synchronized (_portBindingListeners) + { + for(PortBindingListener listener : _portBindingListeners) + { + listener.bound(acceptor, bindAddress); + } + } } public VirtualHostRegistry getVirtualHostRegistry() @@ -556,15 +583,16 @@ public abstract class ApplicationRegistry implements IApplicationRegistry return _securityManager; } - public ManagedObjectRegistry getManagedObjectRegistry() + @Override + public AuthenticationManager getAuthenticationManager(SocketAddress address) { - return _managedObjectRegistry; + return _authenticationManagerRegistry.getAuthenticationManager(address); } @Override - public AuthenticationManager getAuthenticationManager(SocketAddress address) + public IAuthenticationManagerRegistry getAuthenticationManagerRegistry() { - return _authenticationManagerRegistry.getAuthenticationManager(address); + return _authenticationManagerRegistry; } public PluginManager getPluginManager() @@ -581,7 +609,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { return _rootMessageLogger; } - + public RootMessageLogger getCompositeStartupMessageLogger() { return _startupMessageLogger; @@ -597,69 +625,63 @@ public abstract class ApplicationRegistry implements IApplicationRegistry return _qmfService; } - public BrokerConfig getBroker() + public BrokerConfig getBrokerConfig() { - return _broker; + return _brokerConfig; } - public void setBroker(final BrokerConfig broker) + public void setBrokerConfig(final BrokerConfig broker) { - _broker = broker; + _brokerConfig = broker; } public VirtualHost createVirtualHost(final VirtualHostConfiguration vhostConfig) throws Exception { VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig); _virtualHostRegistry.registerVirtualHost(virtualHost); - getBroker().addVirtualHost(virtualHost); + getBrokerConfig().addVirtualHost(virtualHost); return virtualHost; } - + public void registerMessageDelivered(long messageSize) { - if (isStatisticsEnabled()) - { - _messagesDelivered.registerEvent(1L); - _dataDelivered.registerEvent(messageSize); - } + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); } - + public void registerMessageReceived(long messageSize, long timestamp) { - if (isStatisticsEnabled()) - { - _messagesReceived.registerEvent(1L, timestamp); - _dataReceived.registerEvent(messageSize, timestamp); - } + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); } - + public StatisticsCounter getMessageReceiptStatistics() { return _messagesReceived; } - + public StatisticsCounter getDataReceiptStatistics() { return _dataReceived; } - + public StatisticsCounter getMessageDeliveryStatistics() { return _messagesDelivered; } - + public StatisticsCounter getDataDeliveryStatistics() { return _dataDelivered; } - + public void resetStatistics() { _messagesDelivered.reset(); _dataDelivered.reset(); _messagesReceived.reset(); _dataReceived.reset(); - + for (VirtualHost vhost : _virtualHostRegistry.getVirtualHosts()) { vhost.resetStatistics(); @@ -668,25 +690,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public void initialiseStatistics() { - setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && - getConfiguration().isStatisticsGenerationBrokerEnabled()); - _messagesDelivered = new StatisticsCounter("messages-delivered"); _dataDelivered = new StatisticsCounter("bytes-delivered"); _messagesReceived = new StatisticsCounter("messages-received"); _dataReceived = new StatisticsCounter("bytes-received"); } - public boolean isStatisticsEnabled() - { - return _statisticsEnabled; - } - - public void setStatisticsEnabled(boolean enabled) - { - _statisticsEnabled = enabled; - } - private void logStartupMessages(LogActor logActor) { logActor.message(BrokerMessages.STARTUP(QpidProperties.getReleaseVersion(), QpidProperties.getBuildVersion())); @@ -700,4 +709,48 @@ public abstract class ApplicationRegistry implements IApplicationRegistry logActor.message(BrokerMessages.MAX_MEMORY(Runtime.getRuntime().maxMemory())); } + public Broker getBroker() + { + return _broker; + } + + @Override + public void addPortBindingListener(PortBindingListener listener) + { + synchronized (_portBindingListeners) + { + _portBindingListeners.add(listener); + } + } + + + @Override + public boolean useHTTPManagement() + { + return _httpManagementPort != -1; + } + + @Override + public int getHTTPManagementPort() + { + return _httpManagementPort; + } + + public LogRecorder getLogRecorder() + { + return _logRecorder; + } + + @Override + public void addRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener) + { + if(_authenticationManagerRegistry == null) + { + _authManagerChangeListeners.add(registryChangeListener); + } + else + { + _authenticationManagerRegistry.addRegistryChangeListener(registryChangeListener); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index b28e3d6c89..774d0338ef 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -25,8 +25,6 @@ import org.osgi.framework.BundleContext; import org.apache.qpid.AMQException; import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.management.JMXManagedObjectRegistry; -import org.apache.qpid.server.management.NoopManagedObjectRegistry; import java.io.File; @@ -41,18 +39,4 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry { super(new ServerConfiguration(configurationURL), bundleContext); } - - @Override - protected void initialiseManagedObjectRegistry() throws AMQException - { - if (getConfiguration().getManagementEnabled()) - { - setManagedObjectRegistry(new JMXManagedObjectRegistry()); - } - else - { - setManagedObjectRegistry(new NoopManagedObjectRegistry()); - } - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index 35e7fe3f61..2baf7ed5ee 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -27,10 +27,11 @@ import org.apache.qpid.server.configuration.ConfigurationManager; import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.logging.RootMessageLogger; -import org.apache.qpid.server.management.ManagedObjectRegistry; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.IAuthenticationManagerRegistry; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -38,6 +39,7 @@ import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.Map; import java.util.UUID; public interface IApplicationRegistry extends StatisticsGatherer @@ -61,8 +63,6 @@ public interface IApplicationRegistry extends StatisticsGatherer */ ServerConfiguration getConfiguration(); - ManagedObjectRegistry getManagedObjectRegistry(); - /** * Get the AuthenticationManager for the given socket address * @@ -74,6 +74,8 @@ public interface IApplicationRegistry extends StatisticsGatherer */ AuthenticationManager getAuthenticationManager(SocketAddress address); + IAuthenticationManagerRegistry getAuthenticationManagerRegistry(); + VirtualHostRegistry getVirtualHostRegistry(); SecurityManager getSecurityManager(); @@ -95,15 +97,35 @@ public interface IApplicationRegistry extends StatisticsGatherer QMFService getQMFService(); - void setBroker(BrokerConfig broker); + void setBrokerConfig(BrokerConfig broker); + + BrokerConfig getBrokerConfig(); - BrokerConfig getBroker(); + Broker getBroker(); VirtualHost createVirtualHost(VirtualHostConfiguration vhostConfig) throws Exception; ConfigStore getConfigStore(); void setConfigStore(ConfigStore store); - + void initialiseStatisticsReporting(); + + Map getAcceptors(); + + void addPortBindingListener(PortBindingListener listener); + + boolean useHTTPManagement(); + + int getHTTPManagementPort(); + + void addRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener); + + public interface PortBindingListener + { + public void bound(QpidAcceptor acceptor, InetSocketAddress bindAddress); + public void unbound(QpidAcceptor acceptor); + + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java index 7088fae50c..cac60a5283 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java @@ -198,7 +198,7 @@ public abstract class AbstractPasswordFilePrincipalDatabase newUserMap = new HashMap(); BufferedReader reader = null; try @@ -216,7 +216,7 @@ public abstract class AbstractPasswordFilePrincipalDatabase users = _principalDatabase.getUsers(); - - TabularDataSupport userList = new TabularDataSupport(_userlistDataType); - - try - { - // Create the tabular list of message header contents - for (Principal user : users) - { - // Create header attributes list - // Read,Write,Admin items are depcreated and we return always false. - Object[] itemData = {user.getName(), 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; - } - - /*** Broker Methods **/ - - /** - * setPrincipalDatabase - * - * @param database set The Database to use for user lookup - */ - public void setPrincipalDatabase(PrincipalDatabase database) - { - _principalDatabase = database; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java index 0eb3963865..5676c43754 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java @@ -47,7 +47,7 @@ public class AnonymousAuthenticationManager implements AuthenticationManager private static final Principal ANONYMOUS_PRINCIPAL = new UsernamePrincipal("ANONYMOUS"); - private static final Subject ANONYMOUS_SUBJECT = new Subject(); + public static final Subject ANONYMOUS_SUBJECT = new Subject(); static { ANONYMOUS_SUBJECT.getPrincipals().add(ANONYMOUS_PRINCIPAL); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java index 3a1ca4f19d..89a4d8ae66 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java @@ -21,9 +21,12 @@ package org.apache.qpid.server.security.auth.manager; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import org.apache.commons.configuration.ConfigurationException; @@ -49,6 +52,8 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication private final Map _classToAuthManagerMap = new HashMap(); private final AuthenticationManager _defaultAuthenticationManager; private final Map _portToAuthenticationManagerMap; + private final List _listeners = + Collections.synchronizedList(new ArrayList()); public AuthenticationManagerRegistry(ServerConfiguration serverConfiguration, PluginManager _pluginManager) throws ConfigurationException @@ -114,9 +119,8 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication final SecurityConfiguration securityConfiguration) throws ConfigurationException { - for (final Iterator> iterator = factories.iterator(); iterator.hasNext();) + for(AuthenticationManagerPluginFactory factory : factories) { - final AuthenticationManagerPluginFactory factory = (AuthenticationManagerPluginFactory) iterator.next(); final AuthenticationManager tmp = factory.newInstance(securityConfiguration); if (tmp != null) { @@ -127,6 +131,11 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication + " Remove configuration for one of the authentication managers."); } _classToAuthManagerMap.put(tmp.getClass().getSimpleName(),tmp); + + for(RegistryChangeListener listener : _listeners) + { + listener.authenticationManagerRegistered(tmp); + } } } } @@ -179,5 +188,16 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication return portToAuthenticationManagerMap; } + @Override + public Map getAvailableAuthenticationManagers() + { + return Collections.unmodifiableMap(new HashMap(_classToAuthManagerMap)); + } + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) + { + _listeners.add(listener); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java index bfb49b8ed6..485ca2e1e9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java @@ -21,7 +21,9 @@ package org.apache.qpid.server.security.auth.manager; import java.net.SocketAddress; +import java.util.Map; import org.apache.qpid.common.Closeable; +import org.apache.qpid.server.virtualhost.VirtualHost; /** * Registry for {@link AuthenticationManager} instances. @@ -43,4 +45,15 @@ public interface IAuthenticationManagerRegistry extends Closeable * @return authentication manager. */ public AuthenticationManager getAuthenticationManager(SocketAddress address); + + Map getAvailableAuthenticationManagers(); + + public static interface RegistryChangeListener + { + void authenticationManagerRegistered(AuthenticationManager authenticationManager); + void authenticationManagerUnregistered(AuthenticationManager authenticationManager); + } + + public void addRegistryChangeListener(RegistryChangeListener listener); + } \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index 24b365d34c..e6498919a1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -32,7 +32,6 @@ import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; import org.apache.qpid.server.security.auth.sasl.JCAProvider; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; @@ -98,8 +97,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan private PrincipalDatabase _principalDatabase = null; - private AMQUserManagementMBean _mbean = null; - public static final AuthenticationManagerPluginFactory FACTORY = new AuthenticationManagerPluginFactory() { public PrincipalDatabaseAuthenticationManager newInstance(final ConfigurationPlugin config) throws ConfigurationException @@ -211,8 +208,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan { _logger.warn("No additional SASL providers registered."); } - - registerManagement(); } private void initialiseAuthenticationMechanisms(Map> providerMap, PrincipalDatabase database) @@ -332,8 +327,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan { _mechanisms = null; Security.removeProvider(PROVIDER_NAME); - - unregisterManagement(); } private PrincipalDatabase createPrincipalDatabaseImpl(final String pdClazz) throws ConfigurationException @@ -407,6 +400,11 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } + public PrincipalDatabase getPrincipalDatabase() + { + return _principalDatabase; + } + private String generateSetterName(String argName) throws ConfigurationException { if ((argName == null) || (argName.length() == 0)) @@ -427,41 +425,4 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan { _principalDatabase = principalDatabase; } - - protected void registerManagement() - { - try - { - _logger.info("Registering UserManagementMBean"); - - _mbean = new AMQUserManagementMBean(); - _mbean.setPrincipalDatabase(_principalDatabase); - _mbean.register(); - } - catch (Exception e) - { - _logger.warn("User management disabled as unable to create MBean:", e); - _mbean = null; - } - } - - protected void unregisterManagement() - { - try - { - if (_mbean != null) - { - _logger.info("Unregistering UserManagementMBean"); - _mbean.unregister(); - } - } - catch (Exception e) - { - _logger.warn("Failed to unregister User management MBean:", e); - } - finally - { - _mbean = null; - } - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java index e27fd99f90..2e21cfbb07 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java @@ -20,6 +20,9 @@ */ package org.apache.qpid.server.security.auth.rmi; +import java.net.SocketAddress; + +import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; @@ -37,11 +40,13 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator static final String INVALID_CREDENTIALS = "Invalid user details supplied"; static final String CREDENTIALS_REQUIRED = "User details are required. " + "Please ensure you are using an up to date management console to connect."; - + private AuthenticationManager _authenticationManager = null; + private SocketAddress _socketAddress; - public RMIPasswordAuthenticator() + public RMIPasswordAuthenticator(SocketAddress socketAddress) { + _socketAddress = socketAddress; } public void setAuthenticationManager(final AuthenticationManager authenticationManager) @@ -79,11 +84,25 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator { throw new SecurityException(SHOULD_BE_NON_NULL); } - + // Verify that an AuthenticationManager has been set. if (_authenticationManager == null) { - throw new SecurityException(UNABLE_TO_LOOKUP); + try + { + if(ApplicationRegistry.getInstance().getAuthenticationManager(_socketAddress) != null) + { + _authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(_socketAddress); + } + else + { + throw new SecurityException(UNABLE_TO_LOOKUP); + } + } + catch(IllegalStateException e) + { + throw new SecurityException(UNABLE_TO_LOOKUP); + } } final AuthenticationResult result = _authenticationManager.authenticate(username, password); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java index 2bd17cfa2f..f382f90010 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java @@ -33,8 +33,7 @@ public class StatisticsCounter private static final Logger _log = LoggerFactory.getLogger(StatisticsCounter.class); public static final long DEFAULT_SAMPLE_PERIOD = Long.getLong("qpid.statistics.samplePeriod", 2000L); // 2s - public static final boolean DISABLE_STATISTICS = Boolean.getBoolean("qpid.statistics.disable"); - + private static final String COUNTER = "counter"; private static final AtomicLong _counterIds = new AtomicLong(0L); @@ -78,11 +77,6 @@ public class StatisticsCounter public void registerEvent(long value, long timestamp) { - if (DISABLE_STATISTICS) - { - return; - } - long thisSample = (timestamp / _period); synchronized (this) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java index 36fec4025a..37d87bb628 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java @@ -103,16 +103,4 @@ public interface StatisticsGatherer * Reset the counters for this, and any child {@link StatisticsGatherer}s. */ void resetStatistics(); - - /** - * Check if this object has statistics generation enabled. - * - * @return true if statistics generation is enabled - */ - boolean isStatisticsEnabled(); - - /** - * Enable or disable statistics generation for this object. - */ - void setStatisticsEnabled(boolean enabled); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java index 1307b1dbd4..f1053f60ad 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java @@ -32,7 +32,7 @@ public interface ConfigurationRecoveryHandler public static interface QueueRecoveryHandler { - void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments); + void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments, UUID alternateExchangeId); ExchangeRecoveryHandler completeQueueRecovery(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java index 1a67fdf540..7356e1ae83 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java @@ -40,12 +40,7 @@ import org.apache.qpid.server.util.MapJsonSerializer; public class ConfiguredObjectHelper { - /** - * Name of queue attribute to store queue creation arguments. - *

- * This attribute is not defined yet on Queue configured object interface. - */ - private static final String QUEUE_ARGUMENTS = "ARGUMENTS"; + private MapJsonSerializer _serializer = new MapJsonSerializer(); @@ -57,14 +52,15 @@ public class ConfiguredObjectHelper String queueName = (String) attributeMap.get(Queue.NAME); String owner = (String) attributeMap.get(Queue.OWNER); boolean exclusive = (Boolean) attributeMap.get(Queue.EXCLUSIVE); + UUID alternateExchangeId = attributeMap.get(Queue.ALTERNATE_EXCHANGE) == null ? null : UUID.fromString((String)attributeMap.get(Queue.ALTERNATE_EXCHANGE)); @SuppressWarnings("unchecked") - Map queueArgumentsMap = (Map) attributeMap.get(QUEUE_ARGUMENTS); + Map queueArgumentsMap = (Map) attributeMap.get(Queue.ARGUMENTS); FieldTable arguments = null; if (queueArgumentsMap != null) { arguments = FieldTable.convertToFieldTable(queueArgumentsMap); } - qrh.queue(configuredObject.getId(), queueName, owner, exclusive, arguments); + qrh.queue(configuredObject.getId(), queueName, owner, exclusive, arguments, alternateExchangeId); } } @@ -73,6 +69,24 @@ public class ConfiguredObjectHelper Map attributesMap = _serializer.deserialize(queueRecord.getAttributes()); attributesMap.put(Queue.NAME, queue.getName()); attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + if (queue.getAlternateExchange() != null) + { + attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); + } + else + { + attributesMap.remove(Queue.ALTERNATE_EXCHANGE); + } + if (attributesMap.containsKey(Queue.ARGUMENTS)) + { + // We wouldn't need this if createQueueConfiguredObject took only AMQQueue + Map currentArgs = (Map) attributesMap.get(Queue.ARGUMENTS); + currentArgs.putAll(queue.getArguments()); + } + else + { + attributesMap.put(Queue.ARGUMENTS, queue.getArguments()); + } String newJson = _serializer.serialize(attributesMap); ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(queue.getId(), queueRecord.getType(), newJson); return newQueueRecord; @@ -84,9 +98,15 @@ public class ConfiguredObjectHelper attributesMap.put(Queue.NAME, queue.getName()); attributesMap.put(Queue.OWNER, AMQShortString.toString(queue.getOwner())); attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + if (queue.getAlternateExchange() != null) + { + attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); + } + // TODO KW i think the arguments could come from the queue itself removing the need for the parameter arguments. + // It would also do away with the need for the if/then/else within updateQueueConfiguredObject if (arguments != null) { - attributesMap.put(QUEUE_ARGUMENTS, FieldTable.convertToMap(arguments)); + attributesMap.put(Queue.ARGUMENTS, FieldTable.convertToMap(arguments)); } String json = _serializer.serialize(attributesMap); ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(queue.getId(), Queue.class.getName(), json); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java index 7b98b30860..262d7d0213 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java @@ -134,4 +134,10 @@ public class MemoryMessageStore extends NullMessageStore { _eventManager.addEventListener(eventListener, events); } + + @Override + public String getStoreType() + { + return "Memory"; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java index cf08ee00ff..0acaf164d9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -67,4 +67,6 @@ public interface MessageStore extends DurableConfigurationStore void addEventListener(EventListener eventListener, Event... events); String getStoreLocation(); + + String getStoreType(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java index 34c7d2d933..be08e309e6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java @@ -28,7 +28,7 @@ import org.apache.qpid.server.federation.Bridge; import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.queue.AMQQueue; -public class NullMessageStore implements MessageStore +public abstract class NullMessageStore implements MessageStore { @Override public void configureConfigStore(String name, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java index c065eb263b..ab374b4917 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java @@ -232,6 +232,8 @@ public class DerbyMessageStore implements MessageStore private static final String DERBY_SINGLE_DB_SHUTDOWN_CODE = "08006"; + private static final String DERBY_STORE_TYPE = "DERBY"; + private final StateManager _stateManager; private final EventManager _eventManager = new EventManager(); @@ -2651,4 +2653,11 @@ public class DerbyMessageStore implements MessageStore { return _persistentSizeHighThreshold; } + + @Override + public String getStoreType() + { + return DERBY_STORE_TYPE; + } + } \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java index 6b2dff7165..efedad1181 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java @@ -20,10 +20,10 @@ */ package org.apache.qpid.server.subscription; +import org.apache.qpid.server.queue.QueueEntryVisitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import java.util.Iterator; @@ -102,7 +102,7 @@ public class AssignedSubscriptionMessageGroupManager implements MessageGroupMana return visitor.getEntry(); } - private class EntryFinder implements AMQQueue.Visitor + private class EntryFinder implements QueueEntryVisitor { private QueueEntry _entry; private Subscription _sub; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java index 62e94f6f2e..f38e23b342 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java @@ -20,12 +20,12 @@ */ package org.apache.qpid.server.subscription; +import org.apache.qpid.server.queue.QueueEntryVisitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import java.util.HashMap; @@ -176,7 +176,7 @@ public class DefinedGroupMessageGroupManager implements MessageGroupManager return visitor.getEntry(); } - private class EntryFinder implements AMQQueue.Visitor + private class EntryFinder implements QueueEntryVisitor { private QueueEntry _entry; private Subscription _sub; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java index cf2754862d..8f3822be6c 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java @@ -45,7 +45,7 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi final Subscription_0_10 subscription = getSubscription(); if(subscription != null && _entry.isAcquiredBy(_sub)) { - subscription.getSession().acknowledge(subscription, _entry); + subscription.getSessionModel().acknowledge(subscription, _entry); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java index 1e37675c98..826082cc65 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java @@ -72,6 +72,10 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public boolean acquire() { boolean acquired = _entry.acquire(getSubscription()); + if(acquired) + { + getSubscription().recordUnacknowledged(_entry); + } return acquired; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index 66825caa24..8911754a66 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.subscription; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; @@ -32,6 +33,14 @@ public interface Subscription boolean isTransient(); + long getBytesOut(); + + long getMessagesOut(); + + long getUnacknowledgedBytes(); + + long getUnacknowledgedMessages(); + public static enum State { ACTIVE, @@ -45,6 +54,7 @@ public interface Subscription } AMQQueue getQueue(); + AMQSessionModel getSessionModel(); QueueEntry.SubscriptionAcquiredState getOwningState(); QueueEntry.SubscriptionAssignedState getAssignedState(); @@ -108,4 +118,6 @@ public interface Subscription boolean isSessionTransactional(); void queueEmpty() throws AMQException; + + String getConsumerName(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index 1f25c215cc..baf5d09c95 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -44,6 +44,7 @@ import org.apache.qpid.server.logging.subjects.SubscriptionLogSubject; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; @@ -92,8 +93,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage private LogActor _logActor; private UUID _id; private final AtomicLong _deliveredCount = new AtomicLong(0); - private long _createTime = System.currentTimeMillis(); + private final AtomicLong _deliveredBytes = new AtomicLong(0); + + private final AtomicLong _unacknowledgedCount = new AtomicLong(0); + private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); + private long _createTime = System.currentTimeMillis(); + static final class BrowserSubscription extends SubscriptionImpl { @@ -276,22 +282,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public void send(QueueEntry entry, boolean batch) throws AMQException { - // if we do not need to wait for client acknowledgements - // we can decrement the reference count immediately. - - // By doing this _before_ the send we ensure that it - // doesn't get sent if it can't be dequeued, preventing - // duplicate delivery on recovery. - - // The send may of course still fail, in which case, as - // the message is unacked, it will be lost. - + synchronized (getChannel()) { getChannel().getProtocolSession().setDeferFlush(batch); long deliveryTag = getChannel().getNextDeliveryTag(); - + addUnacknowledgedMessage(entry); recordMessageDelivery(entry, deliveryTag); sendToClient(entry, deliveryTag); @@ -371,6 +368,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage } + public AMQSessionModel getSessionModel() + { + return _channel; + } + public ConfigStore getConfigStore() { return getQueue().getConfigStore(); @@ -599,6 +601,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage return _consumerTag; } + public String getConsumerName() + { + return _consumerTag == null ? null : _consumerTag.asString(); + } + public long getSubscriptionID() { return _subscriptionID; @@ -687,6 +694,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { _deliveryMethod.deliverToClient(this,entry,deliveryTag); _deliveredCount.incrementAndGet(); + _deliveredBytes.addAndGet(entry.getSize()); } @@ -832,4 +840,44 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage _channel.getProtocolSession().flushBatched(); } + + public long getBytesOut() + { + return _deliveredBytes.longValue(); + } + + public long getMessagesOut() + { + return _deliveredCount.longValue(); + } + + + protected void addUnacknowledgedMessage(QueueEntry entry) + { + final long size = entry.getSize(); + _unacknowledgedBytes.addAndGet(size); + _unacknowledgedCount.incrementAndGet(); + entry.addStateChangeListener(new QueueEntry.StateChangeListener() + { + public void stateChanged(QueueEntry entry, QueueEntry.State oldState, QueueEntry.State newState) + { + if(oldState.equals(QueueEntry.State.ACQUIRED) && !newState.equals(QueueEntry.State.ACQUIRED)) + { + _unacknowledgedBytes.addAndGet(-size); + _unacknowledgedCount.decrementAndGet(); + entry.removeStateChangeListener(this); + } + } + }); + } + + public long getUnacknowledgedBytes() + { + return _unacknowledgedBytes.longValue(); + } + + public long getUnacknowledgedMessages() + { + return _unacknowledgedCount.longValue(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index 76d975a789..db378f2bf3 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -130,6 +130,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private String _trace; private final long _createTime = System.currentTimeMillis(); private final AtomicLong _deliveredCount = new AtomicLong(0); + private final AtomicLong _deliveredBytes = new AtomicLong(0); + private final AtomicLong _unacknowledgedCount = new AtomicLong(0); + private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); + private final Map _arguments; private int _deferredMessageCredit; private long _deferredSizeCredit; @@ -185,7 +189,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } _queue = queue; - Map arguments = queue.getArguments() == null ? Collections.EMPTY_MAP : queue.getArguments(); + Map arguments = queue.getArguments(); _traceExclude = (String) arguments.get("qpid.trace.exclude"); _trace = (String) arguments.get("qpid.trace.id"); _id = getConfigStore().createId(); @@ -199,9 +203,13 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr CurrentActor.get().message(this, SubscriptionMessages.CREATE(filterLogString, queue.isDurable() && exclusive, filterLogString.length() > 0)); } - } + public String getConsumerName() + { + return _destination; + } + public boolean isSuspended() { return !isActive() || _deleted.get() || _session.isClosing(); // TODO check for Session suspension @@ -620,10 +628,15 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr _session.sendMessage(xfr, _postIdSettingAction); entry.incrementDeliveryCount(); _deliveredCount.incrementAndGet(); + _deliveredBytes.addAndGet(entry.getSize()); if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) { forceDequeue(entry, false); } + else if(_acquireMode == MessageAcquireMode.PRE_ACQUIRED) + { + recordUnacknowledged(entry); + } } else { @@ -632,6 +645,12 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } } + void recordUnacknowledged(QueueEntry entry) + { + _unacknowledgedCount.incrementAndGet(); + _unacknowledgedBytes.addAndGet(entry.getSize()); + } + private void deferredAddCredit(final int deferredMessageCredit, final long deferredSizeCredit) { _deferredMessageCredit += deferredMessageCredit; @@ -653,7 +672,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private void forceDequeue(final QueueEntry entry, final boolean restoreCredit) { - AutoCommitTransaction dequeueTxn = new AutoCommitTransaction(getQueue().getVirtualHost().getMessageStore()); + AutoCommitTransaction dequeueTxn = new AutoCommitTransaction(getQueue().getVirtualHost().getMessageStore()); dequeueTxn.dequeue(entry.getQueue(), entry.getMessage(), new ServerTransaction.Action() { @@ -690,7 +709,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr entry.setRedelivered(); } - if (getSession().isClosing() || !setRedelivered) + if (getSessionModel().isClosing() || !setRedelivered) { entry.decrementDeliveryCount(); } @@ -918,6 +937,8 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr // TODO Fix Store Context / cleanup if(entry.isAcquiredBy(this)) { + _unacknowledgedBytes.addAndGet(-entry.getSize()); + _unacknowledgedCount.decrementAndGet(); entry.discard(); } } @@ -944,7 +965,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return false; } - ServerSession getSession() + public ServerSession getSessionModel() { return _session; } @@ -952,7 +973,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public SessionConfig getSessionConfig() { - return getSession(); + return getSessionModel(); } public boolean isBrowsing() @@ -1073,4 +1094,24 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { _session.getConnection().flush(); } + + public long getBytesOut() + { + return _deliveredBytes.longValue(); + } + + public long getMessagesOut() + { + return _deliveredCount.longValue(); + } + + public long getUnacknowledgedBytes() + { + return _unacknowledgedBytes.longValue(); + } + + public long getUnacknowledgedMessages() + { + return _unacknowledgedCount.longValue(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java index 637ea7dffc..7c4188bfcd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java @@ -20,25 +20,62 @@ */ package org.apache.qpid.server.transport; +import org.apache.qpid.server.protocol.AmqpProtocolVersion; import org.apache.qpid.transport.network.NetworkTransport; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + public class QpidAcceptor { - private NetworkTransport _transport; - private String _protocol; - public QpidAcceptor(NetworkTransport transport, String protocol) + public enum Transport { - _transport = transport; - _protocol = protocol; + TCP("TCP"), + SSL("TCP/SSL"); + + private final String _asString; + + Transport(String asString) + { + _asString = asString; + } + + public String toString() + { + return _asString; + } + } + + private NetworkTransport _networkTransport; + private Transport _transport; + private Set _supported; + + + public QpidAcceptor(NetworkTransport transport, Transport protocol, Set supported) + { + _networkTransport = transport; + _transport = protocol; + _supported = Collections.unmodifiableSet(new HashSet(supported)); } public NetworkTransport getNetworkTransport() + { + return _networkTransport; + } + + public Transport getTransport() { return _transport; } + public Set getSupported() + { + return _supported; + } + public String toString() { - return _protocol; + return _transport.toString(); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java index c9482b9712..2d0e61ec2e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java @@ -37,8 +37,6 @@ import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ConnectionMessages; -import org.apache.qpid.server.management.Managable; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; @@ -56,7 +54,7 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CONNECTIO import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SOCKET_FORMAT; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.USER_FORMAT; -public class ServerConnection extends Connection implements Managable, AMQConnectionModel, LogSubject, AuthorizationHolder +public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject, AuthorizationHolder { private ConnectionConfig _config; private Runnable _onOpenTask; @@ -65,11 +63,9 @@ public class ServerConnection extends Connection implements Managable, AMQConnec private Subject _authorizedSubject = null; private Principal _authorizedPrincipal = null; - private boolean _statisticsEnabled = false; private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final long _connectionId; private final Object _reference = new Object(); - private ServerConnectionMBean _mBean; private VirtualHost _virtualHost; private AtomicLong _lastIoTime = new AtomicLong(); private boolean _blocking; @@ -118,7 +114,6 @@ public class ServerConnection extends Connection implements Managable, AMQConnec { _virtualHost.getConnectionRegistry().deregisterConnection(this); } - unregisterConnectionMbean(); } if (state == State.CLOSED) @@ -156,8 +151,6 @@ public class ServerConnection extends Connection implements Managable, AMQConnec _virtualHost = virtualHost; initialiseStatistics(); - - registerConnectionMbean(); } public void setConnectionConfig(final ConnectionConfig config) @@ -273,7 +266,6 @@ public class ServerConnection extends Connection implements Managable, AMQConnec public void close(AMQConstant cause, String message) throws AMQException { closeSubscriptions(); - unregisterConnectionMbean(); ConnectionCloseCode replyCode = ConnectionCloseCode.NORMAL; try { @@ -338,21 +330,15 @@ public class ServerConnection extends Connection implements Managable, AMQConnec public void registerMessageDelivered(long messageSize) { - if (isStatisticsEnabled()) - { - _messagesDelivered.registerEvent(1L); - _dataDelivered.registerEvent(messageSize); - } + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); _virtualHost.registerMessageDelivered(messageSize); } public void registerMessageReceived(long messageSize, long timestamp) { - if (isStatisticsEnabled()) - { - _messagesReceived.registerEvent(1L, timestamp); - _dataReceived.registerEvent(messageSize, timestamp); - } + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); _virtualHost.registerMessageReceived(messageSize, timestamp); } @@ -386,25 +372,12 @@ public class ServerConnection extends Connection implements Managable, AMQConnec public void initialiseStatistics() { - setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && - _virtualHost.getApplicationRegistry().getConfiguration().isStatisticsGenerationConnectionsEnabled()); - _messagesDelivered = new StatisticsCounter("messages-delivered-" + getConnectionId()); _dataDelivered = new StatisticsCounter("data-delivered-" + getConnectionId()); _messagesReceived = new StatisticsCounter("messages-received-" + getConnectionId()); _dataReceived = new StatisticsCounter("data-received-" + getConnectionId()); } - public boolean isStatisticsEnabled() - { - return _statisticsEnabled; - } - - public void setStatisticsEnabled(boolean enabled) - { - _statisticsEnabled = enabled; - } - /** * @return authorizedSubject */ @@ -448,6 +421,11 @@ public class ServerConnection extends Connection implements Managable, AMQConnec return !super.hasSessionWithName(name); } + public String getRemoteAddressString() + { + return getConfig().getAddress(); + } + public String getUserName() { return _authorizedPrincipal.getName(); @@ -476,12 +454,6 @@ public class ServerConnection extends Connection implements Managable, AMQConnec } } - - public ManagedObject getManagedObject() - { - return _mBean; - } - @Override public void send(ProtocolEvent event) { @@ -489,54 +461,29 @@ public class ServerConnection extends Connection implements Managable, AMQConnec super.send(event); } - public AtomicLong getLastIoTime() + public long getLastIoTime() { - return _lastIoTime; + return _lastIoTime.longValue(); } - void checkForNotification() - { - int channelsCount = getSessionModels().size(); - if (_mBean != null && channelsCount >= getConnectionDelegate().getChannelMax()) - { - _mBean.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); - } - } - - private void registerConnectionMbean() + public String getClientId() { - try - { - _mBean = new ServerConnectionMBean(this); - _mBean.register(); - } - catch (JMException jme) - { - log.error("Unable to register mBean for ServerConnection", jme); - } + return getConnectionDelegate().getClientId(); } - private void unregisterConnectionMbean() + public String getClientVersion() { - if (_mBean != null) - { - if (log.isDebugEnabled()) - { - log.debug("Unregistering mBean for ServerConnection" + _mBean); - } - _mBean.unregister(); - _mBean = null; - } + return getConnectionDelegate().getClientVersion(); } - public String getClientId() + public String getPrincipalAsString() { - return getConnectionDelegate().getClientId(); + return getAuthorizedPrincipal().getName(); } - public String getClientVersion() + public long getSessionCountLimit() { - return getConnectionDelegate().getClientVersion(); + return getChannelMax(); } public Principal getPeerPrincipal() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index ad59c56878..c13f63b44d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -61,7 +61,7 @@ public class ServerConnectionDelegate extends ServerDelegate public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN, AuthenticationManager authManager) { - this(createConnectionProperties(appRegistry.getBroker()), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN, authManager); + this(createConnectionProperties(appRegistry.getBrokerConfig()), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN, authManager); } private ServerConnectionDelegate(Map properties, @@ -226,7 +226,7 @@ public class ServerConnectionDelegate extends ServerDelegate } @Override - protected int getChannelMax() + public int getChannelMax() { return _maxNoOfChannels; } @@ -266,9 +266,6 @@ public class ServerConnectionDelegate extends ServerDelegate if(isSessionNameUnique(atc.getName(), conn)) { super.sessionAttach(conn, atc); - final ServerConnection serverConnection = (ServerConnection) conn; - - serverConnection.checkForNotification(); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java deleted file mode 100644 index bb545164fb..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java +++ /dev/null @@ -1,263 +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.transport; - -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.protocol.AMQSessionModel; - -import javax.management.JMException; -import javax.management.NotCompliantMBeanException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.io.IOException; -import java.util.Date; -import java.util.List; - -/** - * This MBean class implements the management interface. In order to make more attributes, operations and notifications - * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. - */ -@MBeanDescription("Management Bean for an AMQ Broker 0-10 Connection") -public class ServerConnectionMBean extends AbstractAMQManagedConnectionObject -{ - private final ServerConnection _serverConnection; - - @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-10 Connection") - protected ServerConnectionMBean(final ServerConnection serverConnection) throws NotCompliantMBeanException - { - super(serverConnection.getConfig().getAddress()); - _serverConnection = serverConnection; - } - - @Override - public ManagedObject getParentObject() - { - return _serverConnection.getVirtualHost().getManagedObject(); - } - - @Override - public String getClientId() - { - return _serverConnection.getClientId(); - } - - @Override - public String getAuthorizedId() - { - return _serverConnection.getAuthorizedPrincipal().getName(); - } - - @Override - public String getVersion() - { - return String.valueOf(_serverConnection.getClientVersion()); - } - - @Override - public String getRemoteAddress() - { - return _serverConnection.getConfig().getAddress(); - } - - @Override - public Date getLastIoTime() - { - return new Date(_serverConnection.getLastIoTime().longValue()); - } - - @Override - public Long getMaximumNumberOfChannels() - { - return (long) _serverConnection.getConnectionDelegate().getChannelMax(); - } - - @Override - public TabularData channels() throws IOException, JMException - { - final TabularDataSupport channelsList = new TabularDataSupport(_channelsType); - final List list = _serverConnection.getSessionModels(); - - for (final AMQSessionModel channel : list) - { - final ServerSession session = (ServerSession)channel; - Object[] itemValues = - { - session.getChannel(), - session.isTransactional(), - null, - session.getUnacknowledgedMessageCount(), - session.getBlocking() - }; - - final CompositeData channelData = new CompositeDataSupport(_channelType, - COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); - channelsList.put(channelData); - } - return channelsList; - } - - @Override - public void commitTransactions(int channelId) throws JMException - { - final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); - if (session == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - else if (session.isTransactional()) - { - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - session.commit(); - } - finally - { - CurrentActor.remove(); - } - } - } - - @Override - public void rollbackTransactions(int channelId) throws JMException - { - final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); - if (session == null) - { - throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); - } - else if (session.isTransactional()) - { - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - session.rollback(); - } - finally - { - CurrentActor.remove(); - } - } - } - - @Override - public void closeConnection() throws Exception - { - _serverConnection.mgmtClose(); - } - - @Override - public void resetStatistics() throws Exception - { - _serverConnection.resetStatistics(); - } - - @Override - public double getPeakMessageDeliveryRate() - { - return _serverConnection.getMessageDeliveryStatistics().getPeak(); - } - - @Override - public double getPeakDataDeliveryRate() - { - return _serverConnection.getDataDeliveryStatistics().getPeak(); - } - - @Override - public double getMessageDeliveryRate() - { - return _serverConnection.getMessageDeliveryStatistics().getRate(); - } - - @Override - public double getDataDeliveryRate() - { - return _serverConnection.getDataDeliveryStatistics().getRate(); - } - - @Override - public long getTotalMessagesDelivered() - { - return _serverConnection.getMessageDeliveryStatistics().getTotal(); - } - - @Override - public long getTotalDataDelivered() - { - return _serverConnection.getDataDeliveryStatistics().getTotal(); - } - - @Override - public double getPeakMessageReceiptRate() - { - return _serverConnection.getMessageReceiptStatistics().getPeak(); - } - - @Override - public double getPeakDataReceiptRate() - { - return _serverConnection.getDataReceiptStatistics().getPeak(); - } - - @Override - public double getMessageReceiptRate() - { - return _serverConnection.getMessageReceiptStatistics().getRate(); - } - - @Override - public double getDataReceiptRate() - { - return _serverConnection.getDataReceiptStatistics().getRate(); - } - - @Override - public long getTotalMessagesReceived() - { - return _serverConnection.getMessageReceiptStatistics().getTotal(); - } - - @Override - public long getTotalDataReceived() - { - return _serverConnection.getDataReceiptStatistics().getTotal(); - } - - @Override - public boolean isStatisticsEnabled() - { - return _serverConnection.isStatisticsEnabled(); - } - - @Override - public void setStatisticsEnabled(boolean enabled) - { - _serverConnection.setStatisticsEnabled(enabled); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 0cb60fafd3..9914485638 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -630,11 +630,21 @@ public class ServerSession extends Session return _txnRejects.get(); } + public int getChannelId() + { + return getChannel(); + } + public Long getTxnCount() { return _txnCount.get(); } + public Long getTxnStart() + { + return _txnStarts.get(); + } + public Principal getAuthorizedPrincipal() { return getConnection().getAuthorizedPrincipal(); @@ -1059,5 +1069,4 @@ public class ServerSession extends Session { return getId().compareTo(session.getId()); } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 85ea97c107..3dbc835c45 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -1226,11 +1226,7 @@ public class ServerSessionDelegate extends SessionDelegate try { queue = createQueue(queueName, method, virtualHost, (ServerSession)session); - if(method.getExclusive()) - { - queue.setExclusive(true); - } - else if(method.getAutoDelete()) + if(!method.getExclusive() && method.getAutoDelete()) { queue.setDeleteOnNoConsumers(true); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index c59016173a..dcc5acb820 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -32,7 +32,6 @@ import org.apache.qpid.server.connection.IConnectionRegistry; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; @@ -62,8 +61,6 @@ public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHo void close(); - ManagedObject getManagedObject(); - UUID getBrokerId(); void scheduleHouseKeepingTask(long period, HouseKeepingTask task); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index e956806823..acd6101ff8 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -100,7 +100,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa return this; } - public void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments) + public void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments, UUID alternateExchangeId) { try { @@ -111,6 +111,17 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa q = AMQQueueFactory.createAMQQueueImpl(id, queueName, true, owner, false, exclusive, _virtualHost, FieldTable.convertToMap(arguments)); _virtualHost.getQueueRegistry().registerQueue(q); + + if (alternateExchangeId != null) + { + Exchange altExchange = _virtualHost.getExchangeRegistry().getExchange(alternateExchangeId); + if (altExchange == null) + { + _logger.error("Unknown exchange id " + alternateExchangeId + ", cannot set alternate exchange on queue with id " + id); + return; + } + q.setAlternateExchange(altExchange); + } } CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_START(queueName, true)); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 5a56fe1765..8945431e99 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -20,12 +20,20 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.AMQBrokerManagerMBean; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; import org.apache.qpid.server.configuration.ConfigStore; @@ -45,11 +53,9 @@ import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.DefaultQueueRegistry; @@ -66,20 +72,6 @@ import org.apache.qpid.server.txn.DtxRegistry; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPlugin; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; -import javax.management.JMException; -import javax.management.NotCompliantMBeanException; -import javax.management.ObjectName; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - - public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.RegistryChangeListener, EventListener { private static final Logger _logger = Logger.getLogger(VirtualHostImpl.class); @@ -104,10 +96,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr private final VirtualHostConfiguration _vhostConfig; - private final VirtualHostMBean _virtualHostMBean; - - private final AMQBrokerManagerMBean _brokerMBean; - private final QueueRegistry _queueRegistry; private final ExchangeRegistry _exchangeRegistry; @@ -124,8 +112,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr private volatile State _state = State.INITIALISING; - private boolean _statisticsEnabled = false; - private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final Map _linkRegistry = new HashMap(); @@ -144,7 +130,7 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr } _appRegistry = appRegistry; - _brokerConfig = _appRegistry.getBroker(); + _brokerConfig = _appRegistry.getBrokerConfig(); _vhostConfig = hostConfig; _name = _vhostConfig.getName(); _dtxRegistry = new DtxRegistry(); @@ -153,7 +139,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr CurrentActor.get().message(VirtualHostMessages.CREATED(_name)); - _virtualHostMBean = new VirtualHostMBean(); _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); _securityManager.configureHostPlugins(_vhostConfig); @@ -171,8 +156,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr _bindingFactory = new BindingFactory(this); - _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); - _messageStore = initialiseMessageStore(hostConfig.getMessageStoreClass()); configureMessageStore(hostConfig); @@ -541,16 +524,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr CurrentActor.get().message(VirtualHostMessages.CLOSED()); } - public ManagedObject getBrokerMBean() - { - return _brokerMBean; - } - - public ManagedObject getManagedObject() - { - return _virtualHostMBean; - } - public UUID getBrokerId() { return _appRegistry.getBrokerId(); @@ -568,21 +541,15 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr public void registerMessageDelivered(long messageSize) { - if (isStatisticsEnabled()) - { - _messagesDelivered.registerEvent(1L); - _dataDelivered.registerEvent(messageSize); - } + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); _appRegistry.registerMessageDelivered(messageSize); } public void registerMessageReceived(long messageSize, long timestamp) { - if (isStatisticsEnabled()) - { - _messagesReceived.registerEvent(1L, timestamp); - _dataReceived.registerEvent(messageSize, timestamp); - } + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); _appRegistry.registerMessageReceived(messageSize, timestamp); } @@ -621,25 +588,12 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr public void initialiseStatistics() { - setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && - _appRegistry.getConfiguration().isStatisticsGenerationVirtualhostsEnabled()); - _messagesDelivered = new StatisticsCounter("messages-delivered-" + getName()); _dataDelivered = new StatisticsCounter("bytes-delivered-" + getName()); _messagesReceived = new StatisticsCounter("messages-received-" + getName()); _dataReceived = new StatisticsCounter("bytes-received-" + getName()); } - public boolean isStatisticsEnabled() - { - return _statisticsEnabled; - } - - public void setStatisticsEnabled(boolean enabled) - { - _statisticsEnabled = enabled; - } - public BrokerLink createBrokerConnection(UUID id, long createTime, Map arguments) { BrokerLink blink = new BrokerLink(this, id, createTime, arguments); @@ -772,36 +726,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr } } - - /** - * Virtual host JMX MBean class. - * - * This has some of the methods implemented from management interface for exchanges. Any - * Implementation of an Exchange MBean should extend this class. - */ - public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost - { - public VirtualHostMBean() throws NotCompliantMBeanException - { - super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); - } - - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } - - public String getName() - { - return _name; - } - - public VirtualHostImpl getVirtualHost() - { - return VirtualHostImpl.this; - } - } - private final class BeforeActivationListener implements EventListener { @Override @@ -825,17 +749,19 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr public void event(Event event) { State finalState = State.ERRORED; + try { initialiseHouseKeeping(_vhostConfig.getHousekeepingCheckPeriod()); - try - { - _brokerMBean.register(); - } - catch (JMException e) - { - throw new RuntimeException("Failed to register virtual host mbean for virtual host " + getName(), e); - } +//TODO: implement state changing for the VirtualHost MBean instead of registering and unregistering +// try +// { +// _brokerMBean.register(); +// } +// catch (JMException e) +// { +// throw new RuntimeException("Failed to register virtual host mbean for virtual host " + getName(), e); +// } finalState = State.ACTIVE; } finally @@ -861,7 +787,8 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr */ _connectionRegistry.close(IConnectionRegistry.VHOST_PASSIVATE_REPLY_TEXT); - _brokerMBean.unregister(); +//TODO: implement state changing for the VirtualHost MBean instead of registering and unregistering +// _brokerMBean.unregister(); removeHouseKeepingTasks(); _queueRegistry.stopAllAndUnregisterMBeans(); @@ -884,7 +811,6 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr @Override public void event(Event event) { - _brokerMBean.unregister(); shutdownHouseKeeping(); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java index ef621a166a..1be472844a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java @@ -22,10 +22,12 @@ package org.apache.qpid.server.virtualhost; import org.apache.qpid.common.Closeable; import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -37,6 +39,8 @@ public class VirtualHostRegistry implements Closeable private String _defaultVirtualHostName; private ApplicationRegistry _applicationRegistry; + private final Collection _listeners = + Collections.synchronizedCollection(new ArrayList()); public VirtualHostRegistry(ApplicationRegistry applicationRegistry) { @@ -50,11 +54,25 @@ public class VirtualHostRegistry implements Closeable throw new Exception("Virtual Host with name " + host.getName() + " already registered."); } _registry.put(host.getName(),host); + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.virtualHostRegistered(host); + } + } } public synchronized void unregisterVirtualHost(VirtualHost host) { _registry.remove(host.getName()); + synchronized (_listeners) + { + for(RegistryChangeListener listener : _listeners) + { + listener.virtualHostUnregistered(host); + } + } } public VirtualHost getVirtualHost(String name) @@ -106,4 +124,17 @@ public class VirtualHostRegistry implements Closeable } } + + public static interface RegistryChangeListener + { + void virtualHostRegistered(VirtualHost virtualHost); + void virtualHostUnregistered(VirtualHost virtualHost); + + } + + public void addRegistryChangeListener(RegistryChangeListener listener) + { + _listeners.add(listener); + } + } diff --git a/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java deleted file mode 100644 index c06ce5e31a..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java +++ /dev/null @@ -1,395 +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.log4j.xml; - - -import junit.framework.TestCase; -import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -public class QpidLog4JConfiguratorTest extends TestCase -{ - private static final String NEWLINE = System.getProperty("line.separator"); - - private File _testConfigFile; - - private File createTempTestLog4JConfig(String loggerLevel,String rootLoggerLevel, boolean missingTagClose, boolean incorrectAttribute) - { - File tmpFile = null; - try - { - tmpFile = File.createTempFile("QpidLog4JConfiguratorTestLog4jConfig", ".tmp"); - tmpFile.deleteOnExit(); - - FileWriter fstream = new FileWriter(tmpFile); - BufferedWriter writer = new BufferedWriter(fstream); - - writer.write(""+NEWLINE); - writer.write(""+NEWLINE); - - writer.write(""+NEWLINE); - - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - String closeTag="/"; - if(missingTagClose) - { - closeTag=""; - } - - //Example of a 'category' with a 'priority' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - String attributeName="value"; - if(incorrectAttribute) - { - attributeName="values"; - } - - //Example of a 'category' with a 'level' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //Example of a 'logger' with a 'level' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //'root' logger - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - writer.write(""+NEWLINE); - - writer.flush(); - writer.close(); - } - catch (IOException e) - { - fail("Unable to create temporary test log4j configuration"); - } - - return tmpFile; - } - - - - //******* Test Methods ******* // - - public void testCheckLevelsAndStrictParser() - { - //try the valid logger levels - _testConfigFile = createTempTestLog4JConfig("all", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("trace", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("debug", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("warn", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("error", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("fatal", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("off", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("null", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("inherited", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - //now try an invalid logger level - _testConfigFile = createTempTestLog4JConfig("madeup", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - fail("IllegalLoggerLevelException expected, invalid levels used"); - } - catch (IllegalLoggerLevelException e) - { - //expected, ignore - } - catch (IOException e) - { - fail("Incorrect Exception, expected an IllegalLoggerLevelException"); - } - - - - //now try the valid rootLogger levels - _testConfigFile = createTempTestLog4JConfig("info", "all", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "trace", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "info", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "warn", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "error", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "fatal", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "off", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "null", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "inherited", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - } - catch (Exception e) - { - fail("No exception expected, valid levels and xml were used"); - } - - //now try an invalid logger level - _testConfigFile = createTempTestLog4JConfig("info", "madeup", false, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - fail("IllegalLoggerLevelException expected, invalid levels used"); - } - catch (IllegalLoggerLevelException e) - { - //expected, ignore - } - catch (IOException e) - { - fail("Incorrect Exception, expected an IllegalLoggerLevelException"); - } - - - - //now try invalid xml - _testConfigFile = createTempTestLog4JConfig("info", "info", true, false); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - fail("IOException expected, malformed XML used"); - } - catch (IllegalLoggerLevelException e) - { - fail("Incorrect Exception, expected an IOException"); - } - catch (IOException e) - { - //expected, ignore - } - - _testConfigFile = createTempTestLog4JConfig("info", "info", false, true); - try - { - QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath()); - fail("IOException expected, malformed XML used"); - } - catch (IllegalLoggerLevelException e) - { - //expected, ignore - } - catch (IOException e) - { - fail("Incorrect Exception, expected an IllegalLoggerLevelException"); - } - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java deleted file mode 100644 index d34d1bbef3..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java +++ /dev/null @@ -1,194 +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; - -import org.apache.commons.configuration.XMLConfiguration; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.management.common.mbeans.ManagedBroker; -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.logging.SystemOutMessageLogger; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.TestLogActor; -import org.apache.qpid.server.queue.AMQPriorityQueue; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.AMQQueueFactory; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.store.TestableMemoryMessageStore; -import org.apache.qpid.server.util.TestApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.virtualhost.VirtualHostImpl; -import org.apache.qpid.test.utils.QpidTestCase; - -import java.util.HashMap; -import java.util.Map; - -public class AMQBrokerManagerMBeanTest extends QpidTestCase -{ - private QueueRegistry _queueRegistry; - private ExchangeRegistry _exchangeRegistry; - private VirtualHost _vHost; - - public void testExchangeOperations() throws Exception - { - String exchange1 = "testExchange1_" + System.currentTimeMillis(); - String exchange2 = "testExchange2_" + System.currentTimeMillis(); - String exchange3 = "testExchange3_" + System.currentTimeMillis(); - - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null); - - - ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); - mbean.createNewExchange(exchange1, "direct", false); - mbean.createNewExchange(exchange2, "topic", false); - mbean.createNewExchange(exchange3, "headers", false); - - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) != null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) != null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) != null); - - mbean.unregisterExchange(exchange1); - mbean.unregisterExchange(exchange2); - mbean.unregisterExchange(exchange3); - - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange1)) == null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange2)) == null); - assertTrue(_exchangeRegistry.getExchange(new AMQShortString(exchange3)) == null); - } - - public void testQueueOperations() throws Exception - { - String queueName = "testQueue_" + System.currentTimeMillis(); - - ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); - - assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null); - - mbean.createNewQueue(queueName, "test", false); - assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) != null); - - mbean.deleteQueue(queueName); - assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) == null); - } - - public void testCreateNewQueueBindsToDefaultExchange() throws Exception - { - String queueName = "testQueue_" + System.currentTimeMillis(); - - ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); - ExchangeRegistry exReg = _vHost.getExchangeRegistry(); - Exchange defaultExchange = exReg.getDefaultExchange(); - - mbean.createNewQueue(queueName, "test", false); - assertTrue(_queueRegistry.getQueue(new AMQShortString(queueName)) != null); - - assertTrue("New queue should be bound to default exchange", defaultExchange.isBound(new AMQShortString(queueName))); - } - - /** - * Tests that setting the {@link AMQQueueFactory#X_QPID_MAXIMUM_DELIVERY_COUNT} argument does cause the - * maximum delivery count to be set on the Queue. - */ - public void testCreateNewQueueWithMaximumDeliveryCount() throws Exception - { - final Map args = new HashMap(); - args.put(AMQQueueFactory.X_QPID_MAXIMUM_DELIVERY_COUNT, 5); - - final AMQShortString queueName = new AMQShortString("testCreateNewQueueWithMaximumDeliveryCount"); - - final QueueRegistry qReg = _vHost.getQueueRegistry(); - - assertNull("The queue should not yet exist", qReg.getQueue(queueName)); - - final ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); - mbean.createNewQueue(queueName.asString(), "test", false, args); - - final AMQQueue createdQueue = qReg.getQueue(queueName); - assertNotNull("The queue was not registered as expected", createdQueue); - assertEquals("Unexpected maximum delivery count", 5, createdQueue.getMaximumDeliveryCount()); - } - - /** - * Tests that setting the {@link AMQQueueFactory#X_QPID_PRIORITIES} argument prompts creation of - * a Priority Queue. - */ - public void testCreatePriorityQueue() throws Exception - { - int numPriorities = 7; - Map args = new HashMap(); - args.put(AMQQueueFactory.X_QPID_PRIORITIES, numPriorities); - - AMQShortString queueName = new AMQShortString("testCreatePriorityQueue"); - - QueueRegistry qReg = _vHost.getQueueRegistry(); - - assertNull("The queue should not yet exist", qReg.getQueue(queueName)); - - ManagedBroker mbean = new AMQBrokerManagerMBean((VirtualHostImpl.VirtualHostMBean) _vHost.getManagedObject()); - mbean.createNewQueue(queueName.asString(), "test", false, args); - - AMQQueue queue = qReg.getQueue(queueName); - assertEquals("Queue is not a priorty queue", AMQPriorityQueue.class, queue.getClass()); - assertEquals("Number of priorities supported was not as expected", numPriorities, ((AMQPriorityQueue)queue).getPriorities()); - } - - @Override - public void setUp() throws Exception - { - super.setUp(); - - CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); - - XMLConfiguration configXml = new XMLConfiguration(); - configXml.addProperty("virtualhosts.virtualhost(-1).name", "test"); - configXml.addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName()); - - ServerConfiguration configuration = new ServerConfiguration(configXml); - - ApplicationRegistry registry = new TestApplicationRegistry(configuration); - ApplicationRegistry.initialise(registry); - registry.getVirtualHostRegistry().setDefaultVirtualHostName("test"); - - IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); - _vHost = appRegistry.getVirtualHostRegistry().getVirtualHost("test"); - _queueRegistry = _vHost.getQueueRegistry(); - _exchangeRegistry = _vHost.getExchangeRegistry(); - } - - @Override - public void tearDown() throws Exception - { - try - { - super.tearDown(); - } - finally - { - ApplicationRegistry.remove(); - } - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java index 36f131a30f..9225f7810a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/QueueConfigurationTest.java @@ -204,6 +204,18 @@ public class QueueConfigurationTest extends TestCase assertEquals("test-sort-key", qConf.getQueueSortKey()); } + public void testQueueDescription() throws ConfigurationException + { + //Check default value + QueueConfiguration qConf = new QueueConfiguration("test", _emptyConf); + assertNull(qConf.getDescription()); + + // Check explicit value + final VirtualHostConfiguration vhostConfig = overrideConfiguration("description", "mydescription"); + qConf = new QueueConfiguration("test", vhostConfig); + assertEquals("mydescription", qConf.getDescription()); + } + private VirtualHostConfiguration overrideConfiguration(String property, Object value) throws ConfigurationException { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index c2d2eb37c1..2ee02430c8 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -252,13 +252,13 @@ public class ServerConfigurationTest extends QpidTestCase { // Check default _serverConfig.initialise(); - assertEquals(true, _serverConfig.getManagementSSLEnabled()); + assertEquals(false, _serverConfig.getManagementSSLEnabled()); // Check value we set - _config.setProperty("management.ssl.enabled", false); + _config.setProperty("management.ssl.enabled", true); _serverConfig = new ServerConfiguration(_config); _serverConfig.initialise(); - assertEquals(false, _serverConfig.getManagementSSLEnabled()); + assertEquals(true, _serverConfig.getManagementSSLEnabled()); } public void testGetManagementKeystorePassword() throws ConfigurationException @@ -287,25 +287,17 @@ public class ServerConfigurationTest extends QpidTestCase assertEquals(false, _serverConfig.getQueueAutoRegister()); } - public void testGetManagementEnabled() throws ConfigurationException + public void testGetJMXManagementEnabled() throws ConfigurationException { // Check default _serverConfig.initialise(); - assertEquals(true, _serverConfig.getManagementEnabled()); + assertEquals(true, _serverConfig.getJMXManagementEnabled()); // Check value we set _config.setProperty("management.enabled", false); _serverConfig = new ServerConfiguration(_config); _serverConfig.initialise(); - assertEquals(false, _serverConfig.getManagementEnabled()); - } - - public void testSetManagementEnabled() throws ConfigurationException - { - // Check value we set - _serverConfig.initialise(); - _serverConfig.setManagementEnabled(false); - assertEquals(false, _serverConfig.getManagementEnabled()); + assertEquals(false, _serverConfig.getJMXManagementEnabled()); } public void testGetManagementRightsInferAllAccess() throws Exception diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java deleted file mode 100644 index 9034bf9c3a..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java +++ /dev/null @@ -1,235 +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.exchange; - -import org.apache.commons.lang.ArrayUtils; - -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.management.common.mbeans.ManagedExchange; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.model.UUIDGenerator; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.AMQQueueFactory; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.TabularData; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * Unit test class for testing different Exchange MBean operations - */ -public class ExchangeMBeanTest extends InternalBrokerBaseCase -{ - private AMQQueue _queue; - private QueueRegistry _queueRegistry; - private VirtualHost _virtualHost; - - public void testGeneralProperties() throws Exception - { - DirectExchange exchange = new DirectExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - // test general exchange properties - assertEquals("Unexpected exchange name", "amq.direct", mbean.getName()); - assertEquals("Unexpected exchange type", "direct", mbean.getExchangeType()); - assertEquals("Unexpected ticket number", Integer.valueOf(0), mbean.getTicketNo()); - assertFalse("Unexpected durable flag", mbean.isDurable()); - assertTrue("Unexpected auto delete flag", mbean.isAutoDelete()); - } - - public void testDirectExchangeMBean() throws Exception - { - DirectExchange exchange = new DirectExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - mbean.createNewBinding(_queue.getNameShortString().toString(), "binding1"); - mbean.createNewBinding(_queue.getNameShortString().toString(), "binding2"); - - TabularData data = mbean.bindings(); - ArrayList list = new ArrayList(data.values()); - assertTrue(list.size() == 2); - } - - public void testTopicExchangeMBean() throws Exception - { - TopicExchange exchange = new TopicExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - mbean.createNewBinding(_queue.getNameShortString().toString(), "binding1"); - mbean.createNewBinding(_queue.getNameShortString().toString(), "binding2"); - - TabularData data = mbean.bindings(); - ArrayList list = new ArrayList(data.values()); - assertTrue(list.size() == 2); - } - - public void testHeadersExchangeMBean() throws Exception - { - HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,key1=binding1,key2=binding2"); - - TabularData data = mbean.bindings(); - ArrayList list = new ArrayList(data.values()); - assertEquals("Unexpected number of bindings", 1, list.size()); - - final Iterator rowItr = (Iterator) data.values().iterator(); - CompositeDataSupport row = rowItr.next(); - assertBinding(1, _queue.getName(), new String[]{"x-match=any","key1=binding1","key2=binding2"}, row); - } - - /** - * Included to ensure 0-10 Specification compliance: - * 2.3.1.4 "the field in the bind arguments has no value and a field of the same name is present in the message headers - */ - public void testHeadersExchangeMBeanMatchPropertyNoValue() throws Exception - { - HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,key4,key5="); - - TabularData data = mbean.bindings(); - ArrayList list = new ArrayList(data.values()); - assertEquals("Unexpected number of bindings", 1, list.size()); - - final Iterator rowItr = (Iterator) data.values().iterator(); - CompositeDataSupport row = rowItr.next(); - assertBinding(1, _queue.getName(), new String[]{"x-match=any","key4=","key5="}, row); - } - - public void testInvalidHeaderBindingMalformed() throws Exception - { - HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); - ManagedObject managedObj = exchange.getManagedObject(); - ManagedExchange mbean = (ManagedExchange)managedObj; - - try - { - mbean.createNewBinding(_queue.getNameShortString().toString(), "x-match=any,=value4"); - fail("Exception not thrown"); - } - catch (JMException jme) - { - //pass - } - } - - private void assertBinding(final int expectedBindingNo, final String expectedQueueName, final String[] expectedBindingArray, - final CompositeDataSupport row) - { - final Number bindingNumber = (Number) row.get(ManagedExchange.HDR_BINDING_NUMBER); - final String queueName = (String) row.get(ManagedExchange.HDR_QUEUE_NAME); - final String[] bindings = (String[]) row.get(ManagedExchange.HDR_QUEUE_BINDINGS); - assertEquals("Unexpected binding number", expectedBindingNo, bindingNumber); - assertEquals("Unexpected queue name", expectedQueueName, queueName); - assertEquals("Unexpected no of bindings", expectedBindingArray.length, bindings.length); - for(String binding : bindings) - { - assertTrue("Expected binding not found: " + binding, ArrayUtils.contains(expectedBindingArray, binding)); - } - } - - /** - * Test adding bindings and removing them from the default exchange via JMX. - *

- * QPID-2700 - */ - public void testDefaultBindings() throws Exception - { - int bindings = _queue.getBindingCount(); - - Exchange exchange = _queue.getVirtualHost().getExchangeRegistry().getDefaultExchange(); - ManagedExchange mbean = (ManagedExchange) ((AbstractExchange) exchange).getManagedObject(); - - mbean.createNewBinding(_queue.getName(), "robot"); - mbean.createNewBinding(_queue.getName(), "kitten"); - - assertEquals("Should have added two bindings", bindings + 2, _queue.getBindingCount()); - - mbean.removeBinding(_queue.getName(), "robot"); - - assertEquals("Should have one extra binding", bindings + 1, _queue.getBindingCount()); - - mbean.removeBinding(_queue.getName(), "kitten"); - - assertEquals("Should have original number of binding", bindings, _queue.getBindingCount()); - } - - /** - * Test adding bindings and removing them from the topic exchange via JMX. - *

- * QPID-2700 - */ - public void testTopicBindings() throws Exception - { - int bindings = _queue.getBindingCount(); - - Exchange exchange = _queue.getVirtualHost().getExchangeRegistry().getExchange(new AMQShortString("amq.topic")); - ManagedExchange mbean = (ManagedExchange) ((AbstractExchange) exchange).getManagedObject(); - - mbean.createNewBinding(_queue.getName(), "robot.#"); - mbean.createNewBinding(_queue.getName(), "#.kitten"); - - assertEquals("Should have added two bindings", bindings + 2, _queue.getBindingCount()); - - mbean.removeBinding(_queue.getName(), "robot.#"); - - assertEquals("Should have one extra binding", bindings + 1, _queue.getBindingCount()); - - mbean.removeBinding(_queue.getName(), "#.kitten"); - - assertEquals("Should have original number of binding", bindings, _queue.getBindingCount()); - } - - @Override - public void setUp() throws Exception - { - super.setUp(); - - IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(); - _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); - _queueRegistry = _virtualHost.getQueueRegistry(); - _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, false, - _virtualHost, null); - _queueRegistry.registerQueue(_queue); - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java index 4305cdadc6..833df34fd8 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.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 @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.exchange; +import java.util.Collection; import junit.framework.TestCase; import org.apache.qpid.server.binding.Binding; @@ -50,6 +51,16 @@ public class HeadersBindingTest extends TestCase return 0; } + public String getUserId() + { + return null; + } + + public String getAppId() + { + return null; + } + public String getMessageId() { return null; @@ -57,7 +68,7 @@ public class HeadersBindingTest extends TestCase public String getMimeType() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } public String getEncoding() @@ -105,6 +116,12 @@ public class HeadersBindingTest extends TestCase return _headers.keySet().containsAll(names); } + @Override + public Collection getHeaderNames() + { + return _headers.keySet(); + } + public boolean containsHeader(String name) { return _headers.containsKey(name); @@ -125,13 +142,13 @@ public class HeadersBindingTest extends TestCase private MockHeader matchHeaders = new MockHeader(); private int _count = 0; private MockAMQQueue _queue; - + protected void setUp() { _count++; _queue = new MockAMQQueue(getQueueName()); } - + protected String getQueueName() { return "Queue" + _count; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java deleted file mode 100644 index f9ad81ae74..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java +++ /dev/null @@ -1,428 +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.logging.management; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -import org.apache.qpid.server.util.InternalBrokerBaseCase; - -import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_LEVEL; -import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_NAME; - -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularDataSupport; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class LoggingManagementMBeanTest extends InternalBrokerBaseCase -{ - private static final String TEST_LOGGER = "LoggingManagementMBeanTestLogger"; - private static final String TEST_LOGGER_CHILD1 = "LoggingManagementMBeanTestLogger.child1"; - private static final String TEST_LOGGER_CHILD2 = "LoggingManagementMBeanTestLogger.child2"; - - private static final String TEST_CATEGORY_PRIORITY = "LogManMBeanTest.category.priority"; - private static final String TEST_CATEGORY_LEVEL = "LogManMBeanTest.category.level"; - private static final String TEST_LOGGER_LEVEL = "LogManMBeanTest.logger.level"; - - private static final String NEWLINE = System.getProperty("line.separator"); - - private File _testConfigFile; - - @Override - public void setUp() throws Exception - { - super.setUp(); - _testConfigFile = createTempTestLog4JConfig(); - } - - @Override - public void tearDown() throws Exception - { - File oldTestConfigFile = new File(_testConfigFile.getAbsolutePath() + ".old"); - if(oldTestConfigFile.exists()) - { - oldTestConfigFile.delete(); - } - - _testConfigFile.delete(); - - super.tearDown(); - } - - private File createTempTestLog4JConfig() - { - File tmpFile = null; - try - { - tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp"); - tmpFile.deleteOnExit(); - - FileWriter fstream = new FileWriter(tmpFile); - BufferedWriter writer = new BufferedWriter(fstream); - - writer.write(""+NEWLINE); - writer.write(""+NEWLINE); - - writer.write(""+NEWLINE); - - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //Example of a 'category' with a 'priority' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //Example of a 'category' with a 'level' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //Example of a 'logger' with a 'level' - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - //'root' logger - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - writer.write(" "+NEWLINE); - - writer.write(""+NEWLINE); - - writer.flush(); - writer.close(); - } - catch (IOException e) - { - fail("Unable to create temporary test log4j configuration"); - } - - return tmpFile; - } - - - - //******* Test Methods ******* // - - public void testSetRuntimeLoggerLevel() - { - LoggingManagementMBean lm = null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - //create a parent test logger, set its level explicitly - Logger log = Logger.getLogger(TEST_LOGGER); - log.setLevel(Level.toLevel("info")); - - //create child1 test logger, check its *effective* level is the same as the parent, "info" - Logger log1 = Logger.getLogger(TEST_LOGGER_CHILD1); - assertTrue("Test logger's level was not the expected value", - log1.getEffectiveLevel().toString().equalsIgnoreCase("info")); - - //now change its level to "warn" - assertTrue("Failed to set logger level", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "warn")); - - //check the change, see its actual level is "warn - assertTrue("Test logger's level was not the expected value", - log1.getLevel().toString().equalsIgnoreCase("warn")); - - //try an invalid level - assertFalse("Trying to set an invalid level succeded", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "made.up.level")); - } - - public void testSetRuntimeRootLoggerLevel() - { - LoggingManagementMBean lm = null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - Logger log = Logger.getRootLogger(); - - //get current root logger level - Level origLevel = log.getLevel(); - - //change level twice to ensure a new level is actually selected - - //set root loggers level to info - assertTrue("Failed to set root logger level", lm.setRuntimeRootLoggerLevel("debug")); - //check it is now actually info - Level currentLevel = log.getLevel(); - assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("debug"))); - - //try an invalid level - assertFalse("Trying to set an invalid level succeded", lm.setRuntimeRootLoggerLevel("made.up.level")); - - //set root loggers level to warn - assertTrue("Failed to set logger level", lm.setRuntimeRootLoggerLevel("info")); - //check it is now actually warn - currentLevel = log.getLevel(); - assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("info"))); - - //restore original level - log.setLevel(origLevel); - } - - public void testGetRuntimeRootLoggerLevel() - { - LoggingManagementMBean lm = null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - Logger log = Logger.getRootLogger(); - - //get current root logger level - Level origLevel = log.getLevel(); - - //change level twice to ensure a new level is actually selected - - //set root loggers level to debug - log.setLevel(Level.toLevel("debug")); - //check it is now actually debug - assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("debug")); - - - //set root loggers level to warn - log.setLevel(Level.toLevel("info")); - //check it is now actually warn - assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("info")); - - //restore original level - log.setLevel(origLevel); - } - - public void testViewEffectiveRuntimeLoggerLevels() - { - LoggingManagementMBean lm = null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - //(re)create a parent test logger, set its level explicitly - Logger log = Logger.getLogger(TEST_LOGGER); - log.setLevel(Level.toLevel("info")); - - //retrieve the current effective runtime logger level values - TabularDataSupport levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); - Collection records = levels.values(); - Map list = new HashMap(); - for (Object o : records) - { - CompositeData data = (CompositeData) o; - list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); - } - - //check child2 does not exist already - assertFalse("Did not expect this logger to exist already", list.containsKey(TEST_LOGGER_CHILD2)); - - //create child2 test logger - Logger log2 = Logger.getLogger(TEST_LOGGER_CHILD2); - - //retrieve the current effective runtime logger level values - levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); - records = levels.values(); - list = new HashMap(); - for (Object o : records) - { - CompositeData data = (CompositeData) o; - list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); - } - - //verify the parent and child2 loggers are present in returned values - assertTrue(TEST_LOGGER + " logger was not in the returned list", list.containsKey(TEST_LOGGER)); - assertTrue(TEST_LOGGER_CHILD2 + " logger was not in the returned list", list.containsKey(TEST_LOGGER_CHILD2)); - - //check child2's effective level is the same as the parent, "info" - assertTrue("Test logger's level was not the expected value", - list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("info")); - - //now change its level explicitly to "warn" - log2.setLevel(Level.toLevel("warn")); - - //retrieve the current effective runtime logger level values - levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels(); - records = levels.values(); - list = new HashMap(); - for (Object o : records) - { - CompositeData data = (CompositeData) o; - list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); - } - - //check child2's effective level is now "warn" - assertTrue("Test logger's level was not the expected value", - list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("warn")); - } - - public void testViewAndSetConfigFileLoggerLevel() throws Exception - { - LoggingManagementMBean lm =null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - //retrieve the current values - TabularDataSupport levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels(); - Collection records = levels.values(); - Map list = new HashMap(); - for (Object o : records) - { - CompositeData data = (CompositeData) o; - list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); - } - - //check the 3 different types of logger definition are successfully retrieved before update - assertTrue("Wrong number of items in returned list", list.size() == 3); - assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY)); - assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL)); - assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL)); - - //check that their level is as expected - assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("info")); - assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("warn")); - assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("error")); - - //increase their levels a notch to test the 3 different types of logger definition are successfully updated - //change the category+priority to warn - assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_PRIORITY, "warn")); - //change the category+level to error - assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_LEVEL, "error")); - //change the logger+level to trace - assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "trace")); - - //try an invalid level - assertFalse("Use of an invalid logger level was successfull", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "made.up.level")); - - //try an invalid logger name - assertFalse("Use of an invalid logger name was successfull", lm.setConfigFileLoggerLevel("made.up.logger.name", "info")); - - //retrieve the new values from the file and check them - levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels(); - records = levels.values(); - list = new HashMap(); - for (Object o : records) - { - CompositeData data = (CompositeData) o; - list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString()); - } - - //check the 3 different types of logger definition are successfully retrieved after update - assertTrue("Wrong number of items in returned list", list.size() == 3); - assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY)); - assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL)); - assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL)); - - //check that their level is as expected after the changes - assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("warn")); - assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("error")); - assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("trace")); - } - - public void testGetAndSetConfigFileRootLoggerLevel() throws Exception - { - LoggingManagementMBean lm =null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - //retrieve the current value - String level = lm.getConfigFileRootLoggerLevel(); - - //check the value was successfully retrieved before update - assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("info")); - - //try an invalid level - assertFalse("Use of an invalid RootLogger level was successfull", lm.setConfigFileRootLoggerLevel("made.up.level")); - - //change the level to warn - assertTrue("Failed to set new RootLogger level", lm.setConfigFileRootLoggerLevel("warn")); - - //retrieve the current value - level = lm.getConfigFileRootLoggerLevel(); - - //check the value was successfully retrieved after update - assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("warn")); - } - - public void testGetLog4jLogWatchInterval() - { - LoggingManagementMBean lm =null; - try - { - lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 5000); - } - catch (JMException e) - { - fail("Could not create test LoggingManagementMBean"); - } - - assertTrue("Wrong value returned for logWatch period", lm.getLog4jLogWatchInterval() == 5000); - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java deleted file mode 100644 index f7d85c11a8..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java +++ /dev/null @@ -1,153 +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.management; - -import org.apache.qpid.management.common.mbeans.UserManagement; -import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; -import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; -import org.apache.qpid.server.util.InternalBrokerBaseCase; - -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularData; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -/** - * - * Tests the AMQUserManagementMBean and its interaction with the PrincipalDatabase. - * - */ -public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase -{ - private PlainPasswordFilePrincipalDatabase _database; - private AMQUserManagementMBean _amqumMBean; - - private File _passwordFile; - - private static final String TEST_USERNAME = "testuser"; - private static final String TEST_PASSWORD = "password"; - - @Override - public void setUp() throws Exception - { - super.setUp(); - - _database = new PlainPasswordFilePrincipalDatabase(); - _amqumMBean = new AMQUserManagementMBean(); - loadFreshTestPasswordFile(); - } - - @Override - public void tearDown() throws Exception - { - //clean up test password/access files - File _oldPasswordFile = new File(_passwordFile.getAbsolutePath() + ".old"); - _oldPasswordFile.delete(); - _passwordFile.delete(); - - super.tearDown(); - } - - public void testDeleteUser() - { - assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); - assertTrue("Delete should return true to flag successful delete", _amqumMBean.deleteUser(TEST_USERNAME)); - assertEquals("Unexpected number of users after test", 0,_amqumMBean.viewUsers().size()); - } - - public void testDeleteUserWhereUserDoesNotExist() - { - assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); - assertFalse("Delete should return false to flag unsuccessful delete", _amqumMBean.deleteUser("made.up.username")); - assertEquals("Unexpected number of users after test", 1,_amqumMBean.viewUsers().size()); - - } - - public void testCreateUser() - { - assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); - assertTrue("Create should return true to flag successful create", _amqumMBean.createUser("newuser", "mypass")); - assertEquals("Unexpected number of users before test", 2,_amqumMBean.viewUsers().size()); - } - - public void testCreateUserWhereUserAlreadyExists() - { - assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); - assertFalse("Create should return false to flag unsuccessful create", _amqumMBean.createUser(TEST_USERNAME, "mypass")); - assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); - } - - public void testSetPassword() - { - assertTrue("Set password should return true to flag successful change", _amqumMBean.setPassword(TEST_USERNAME, "newpassword")); - } - - public void testSetPasswordWhereUserDoesNotExist() - { - assertFalse("Set password should return false to flag successful change", _amqumMBean.setPassword("made.up.username", "newpassword")); - } - - public void testViewUsers() - { - TabularData userList = _amqumMBean.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)); - } - - // ============================ Utility methods ========================= - - private void loadFreshTestPasswordFile() - { - try - { - if(_passwordFile == null) - { - _passwordFile = File.createTempFile(this.getClass().getName(),".password"); - } - - BufferedWriter passwordWriter = new BufferedWriter(new FileWriter(_passwordFile, false)); - passwordWriter.write(TEST_USERNAME + ":" + TEST_PASSWORD); - passwordWriter.newLine(); - passwordWriter.flush(); - passwordWriter.close(); - _database.setPasswordFile(_passwordFile.toString()); - _amqumMBean.setPrincipalDatabase(_database); - } - catch (IOException e) - { - fail("Unable to create test password file: " + e.getMessage()); - } - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java deleted file mode 100644 index fe9bcc57a6..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java +++ /dev/null @@ -1,146 +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.protocol; - -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.management.common.mbeans.ManagedConnection; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.AMQQueueFactory; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.TestableMemoryMessageStore; -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularData; - - -/** Test class to test MBean operations for AMQMinaProtocolSession. */ -public class AMQProtocolSessionMBeanTest extends InternalBrokerBaseCase -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class); - - private MessageStore _messageStore = new TestableMemoryMessageStore(); - private AMQProtocolEngine _protocolSession; - private AMQChannel _channel; - private AMQProtocolSessionMBean _mbean; - - public void testChannels() throws Exception - { - // check the channel count is correct - int channelCount = _mbean.channels().size(); - assertTrue(channelCount == 1); - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue_" + System.currentTimeMillis()), - false, - new AMQShortString("test"), - true, - false, _protocolSession.getVirtualHost(), null); - AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore); - channel.setDefaultQueue(queue); - _protocolSession.addChannel(channel); - channelCount = _mbean.channels().size(); - assertTrue(channelCount == 2); - - // general properties test - _protocolSession.setMaximumNumberOfChannels(1000L); - assertTrue(_mbean.getMaximumNumberOfChannels() == 1000L); - - // check APIs - AMQChannel channel3 = new AMQChannel(_protocolSession, 3, _messageStore); - channel3.setLocalTransactional(); - _protocolSession.addChannel(channel3); - _mbean.rollbackTransactions(2); - _mbean.rollbackTransactions(3); - _mbean.commitTransactions(2); - _mbean.commitTransactions(3); - - // This should throw exception, because the channel does't exist - try - { - _mbean.commitTransactions(4); - fail(); - } - catch (JMException ex) - { - log.debug("expected exception is thrown :" + ex.getMessage()); - } - - // check channels() return type conveys flow control blocking status correctly - AMQChannel channel4 = new AMQChannel(_protocolSession, 4, _messageStore); - _protocolSession.addChannel(channel4); - channel4.setDefaultQueue(queue); - - final String blocking = ManagedConnection.FLOW_BLOCKED; - TabularData channels = _mbean.channels(); - CompositeData chan4result = channels.get(new Integer[]{4}); - assertNotNull(chan4result); - assertEquals("Flow should not have been blocked", false, chan4result.get(blocking)); - - channel4.block(queue); - channels = _mbean.channels(); - chan4result = channels.get(new Integer[]{4}); - assertNotNull(chan4result); - assertEquals("Flow should have been blocked", true, chan4result.get(blocking)); - - channel4.unblock(queue); - channels = _mbean.channels(); - chan4result = channels.get(new Integer[]{4}); - assertNotNull(chan4result); - assertEquals("Flow should have been unblocked", false, chan4result.get(blocking)); - - // check if closing of session works - _protocolSession.addChannel(new AMQChannel(_protocolSession, 5, _messageStore)); - _mbean.closeConnection(); - try - { - channelCount = _mbean.channels().size(); - assertTrue(channelCount == 0); - // session is now closed so adding another channel should throw an exception - _protocolSession.addChannel(new AMQChannel(_protocolSession, 6, _messageStore)); - fail(); - } - catch (AMQException ex) - { - log.debug("expected exception is thrown :" + ex.getMessage()); - } - } - - @Override - public void setUp() throws Exception - { - super.setUp(); - - VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); - _protocolSession = new InternalTestProtocolSession(vhost); - - _channel = new AMQChannel(_protocolSession, 1, _messageStore); - _protocolSession.addChannel(_channel); - _mbean = (AMQProtocolSessionMBean) _protocolSession.getManagedObject(); - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java deleted file mode 100644 index 25d35aab16..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.message.AMQMessage; -import org.apache.qpid.server.message.MessageMetaData; -import org.apache.qpid.server.protocol.InternalTestProtocolSession; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; -import org.apache.qpid.server.util.InternalBrokerBaseCase; - -import javax.management.Notification; - -import java.nio.ByteBuffer; -import java.util.ArrayList; - -/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ -public class AMQQueueAlertTest extends InternalBrokerBaseCase -{ - private final static long MAX_MESSAGE_COUNT = 50; - private final static long MAX_MESSAGE_AGE = 250; // 0.25 sec - private final static long MAX_MESSAGE_SIZE = 2000; // 2 KB - private final static long MAX_QUEUE_DEPTH = 10000; // 10 KB - private AMQQueueMBean _queueMBean; - private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE; - - /** - * Tests if the alert gets thrown when message count increases the threshold limit - * - * @throws Exception - */ - public void testMessageCountAlert() throws Exception - { - setSession(new InternalTestProtocolSession(getVirtualHost())); - AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore()); - getSession().addChannel(channel); - - setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"), - false, false, - getVirtualHost(), null)); - _queueMBean = (AMQQueueMBean) getQueue().getManagedObject(); - - _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); - - sendMessages(channel, MAX_MESSAGE_COUNT, 256l); - assertTrue(_queueMBean.getMessageCount() == MAX_MESSAGE_COUNT); - - Notification lastNotification = _queueMBean.getLastNotification(); - assertNotNull(lastNotification); - - String notificationMsg = lastNotification.getMessage(); - assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_COUNT_ALERT.name())); - } - - /** - * Tests if the Message Size alert gets thrown when message of higher than threshold limit is sent - * - * @throws Exception - */ - public void testMessageSizeAlert() throws Exception - { - setSession(new InternalTestProtocolSession(getVirtualHost())); - AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore()); - getSession().addChannel(channel); - - setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"), - false, false, - getVirtualHost(), null)); - _queueMBean = (AMQQueueMBean) getQueue().getManagedObject(); - _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); - _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE); - - sendMessages(channel, 1, MAX_MESSAGE_SIZE * 2); - assertTrue(_queueMBean.getMessageCount() == 1); - - Notification lastNotification = _queueMBean.getLastNotification(); - assertNotNull(lastNotification); - - String notificationMsg = lastNotification.getMessage(); - assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_SIZE_ALERT.name())); - } - - /** - * Tests if Queue Depth alert is thrown when queue depth reaches the threshold value - * - * Based on FT-402 subbmitted by client - * - * @throws Exception - */ - public void testQueueDepthAlertNoSubscriber() throws Exception - { - setSession(new InternalTestProtocolSession(getVirtualHost())); - AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore()); - getSession().addChannel(channel); - - setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"), - false, false, - getVirtualHost(), null)); - _queueMBean = (AMQQueueMBean) getQueue().getManagedObject(); - _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); - _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH); - - while (getQueue().getQueueDepth() < MAX_QUEUE_DEPTH) - { - sendMessages(channel, 1, MAX_MESSAGE_SIZE); - } - - Notification lastNotification = _queueMBean.getLastNotification(); - assertNotNull(lastNotification); - - String notificationMsg = lastNotification.getMessage(); - assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name())); - } - - /** - * Tests if MESSAGE AGE alert is thrown, when a message is in the queue for time higher than threshold value of - * message age - * - * Alternative test to FT-401 provided by client - * - * @throws Exception - */ - public void testMessageAgeAlert() throws Exception - { - setSession(new InternalTestProtocolSession(getVirtualHost())); - AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore()); - getSession().addChannel(channel); - - setQueue(AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"), - false, false, - getVirtualHost(), null)); - _queueMBean = (AMQQueueMBean) getQueue().getManagedObject(); - _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); - _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE); - - sendMessages(channel, 1, MAX_MESSAGE_SIZE); - - // Ensure message sits on queue long enough to age. - Thread.sleep(MAX_MESSAGE_AGE * 2); - - Notification lastNotification = _queueMBean.getLastNotification(); - assertNotNull("Last notification was null", lastNotification); - - String notificationMsg = lastNotification.getMessage(); - assertTrue(notificationMsg.startsWith(NotificationCheck.MESSAGE_AGE_ALERT.name())); - } - - /* - This test sends some messages to the queue with subscribers needing message to be acknowledged. - The messages will not be acknowledged and will be required twice. Why we are checking this is because - the bug reported said that the queueDepth keeps increasing when messages are requeued. - // TODO - queue depth now includes unacknowledged messages so does not go down when messages are delivered - - The QueueDepth should decrease when messages are delivered from the queue (QPID-408) - */ - public void testQueueDepthAlertWithSubscribers() throws Exception - { - AMQChannel channel = new AMQChannel(getSession(), 2, getMessageStore()); - getSession().addChannel(channel); - - // Create queue - setQueue(getNewQueue()); - Subscription subscription = - SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), getSession(), new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); - - getQueue().registerSubscription( - subscription, false); - - _queueMBean = (AMQQueueMBean) getQueue().getManagedObject(); - _queueMBean.setMaximumMessageCount(9999l); // Set a high value, because this is not being tested - _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH); - - // Send messages(no of message to be little more than what can cause a Queue_Depth alert) - int messageCount = Math.round(MAX_QUEUE_DEPTH / MAX_MESSAGE_SIZE) + 10; - long totalSize = (messageCount * MAX_MESSAGE_SIZE); - sendMessages(channel, messageCount, MAX_MESSAGE_SIZE); - - // Check queueDepth. There should be no messages on the queue and as the subscriber is listening - // so there should be no Queue_Deoth alert raised - assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth())); - Notification lastNotification = _queueMBean.getLastNotification(); -// assertNull(lastNotification); - - // Kill the subscriber and check for the queue depth values. - // Messages are unacknowledged, so those should get requeued. All messages should be on the Queue - getQueue().unregisterSubscription(subscription); - channel.requeue(); - - assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth())); - - lastNotification = _queueMBean.getLastNotification(); - assertNotNull(lastNotification); - String notificationMsg = lastNotification.getMessage(); - assertTrue(notificationMsg.startsWith(NotificationCheck.QUEUE_DEPTH_ALERT.name())); - - // Connect a consumer again and check QueueDepth values. The queue should get emptied. - // Messages will get delivered but still are unacknowledged. - Subscription subscription2 = - SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), getSession(), new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); - - getQueue().registerSubscription( - subscription2, false); - - while (getQueue().getUndeliveredMessageCount()!= 0) - { - Thread.sleep(100); - } -// assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth())); - - // Kill the subscriber again. Now those messages should get requeued again. Check if the queue depth - // value is correct. - getQueue().unregisterSubscription(subscription2); - channel.requeue(); - - assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth())); - getSession().closeSession(); - - // Check the clear queue - _queueMBean.clearQueue(); - assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth())); - } - - protected IncomingMessage message(final boolean immediate, long size) throws AMQException - { - MessagePublishInfo publish = new MessagePublishInfo() - { - - public AMQShortString getExchange() - { - return null; - } - - public void setExchange(AMQShortString exchange) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isImmediate() - { - return immediate; - } - - public boolean isMandatory() - { - return false; - } - - public AMQShortString getRoutingKey() - { - return null; - } - }; - - ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); - BasicContentHeaderProperties props = new BasicContentHeaderProperties(); - contentHeaderBody.setProperties(props); - contentHeaderBody.setBodySize(size); // in bytes - IncomingMessage message = new IncomingMessage(publish); - message.setContentHeaderBody(contentHeaderBody); - - return message; - } - - @Override - protected void configure() - { - // Increase Alert Check period - getConfiguration().setHousekeepingCheckPeriod(200); - } - - private void sendMessages(AMQChannel channel, long messageCount, final long size) throws AMQException - { - IncomingMessage[] messages = new IncomingMessage[(int) messageCount]; - MessageMetaData[] metaData = new MessageMetaData[(int) messageCount]; - for (int i = 0; i < messages.length; i++) - { - messages[i] = message(false, size); - ArrayList qs = new ArrayList(); - qs.add(getQueue()); - metaData[i] = messages[i].headersReceived(System.currentTimeMillis()); - messages[i].setStoredMessage(getMessageStore().addMessage(metaData[i])); - - messages[i].enqueue(qs); - - } - - for (int i = 0; i < messageCount; i++) - { - ContentChunk contentChunk = new ContentChunk() - { - private byte[] _data = new byte[(int)size]; - - public int getSize() - { - return (int) size; - } - - public byte[] getData() - { - return _data; - } - - public void reduceToFit() - { - } - }; - - messages[i].addContentBodyFrame(contentChunk); - messages[i].getStoredMessage().addContent(0, ByteBuffer.wrap(contentChunk.getData())); - - getQueue().enqueue(new AMQMessage(messages[i].getStoredMessage())); - } - } - - private AMQQueue getNewQueue() throws AMQException - { - return AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue" + Math.random()), - false, - new AMQShortString("AMQueueAlertTest"), - false, - false, getVirtualHost(), null); - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java deleted file mode 100644 index 89c14c40a0..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java +++ /dev/null @@ -1,546 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.qpid.server.queue; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.text.SimpleDateFormat; -import java.util.*; -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.TabularData; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.management.common.mbeans.ManagedQueue; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.message.AMQMessage; -import org.apache.qpid.server.message.MessageMetaData; -import org.apache.qpid.server.protocol.InternalTestProtocolSession; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.TestableMemoryMessageStore; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionFactory; -import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; -import org.apache.qpid.server.util.InternalBrokerBaseCase; - -/** - * Test class to test AMQQueueMBean attributes and operations - */ -public class AMQQueueMBeanTest extends InternalBrokerBaseCase -{ - private static long MESSAGE_SIZE = 1000; - private AMQQueueMBean _queueMBean; - private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE; - - public void testMessageCountTransient() throws Exception - { - int messageCount = 10; - sendMessages(messageCount, false); - assertTrue(_queueMBean.getMessageCount() == messageCount); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - long queueDepth = (messageCount * MESSAGE_SIZE); - assertTrue(_queueMBean.getQueueDepth() == queueDepth); - - _queueMBean.deleteMessageFromTop(); - assertTrue(_queueMBean.getMessageCount() == (messageCount - 1)); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - - _queueMBean.clearQueue(); - assertEquals(0,(int)_queueMBean.getMessageCount()); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - - //Ensure that the data has been removed from the Store - verifyBrokerState(); - } - - public void testMessageCountPersistent() throws Exception - { - int messageCount = 10; - sendMessages(messageCount, true); - assertEquals("", messageCount, _queueMBean.getMessageCount().intValue()); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - long queueDepth = (messageCount * MESSAGE_SIZE); - assertTrue(_queueMBean.getQueueDepth() == queueDepth); - - _queueMBean.deleteMessageFromTop(); - assertTrue(_queueMBean.getMessageCount() == (messageCount - 1)); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - - _queueMBean.clearQueue(); - assertTrue(_queueMBean.getMessageCount() == 0); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - - //Ensure that the data has been removed from the Store - verifyBrokerState(); - } - - public void testDeleteMessages() throws Exception - { - int messageCount = 10; - sendMessages(messageCount, true); - assertEquals("", messageCount, _queueMBean.getMessageCount().intValue()); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - long queueDepth = (messageCount * MESSAGE_SIZE); - assertTrue(_queueMBean.getQueueDepth() == queueDepth); - - //delete first message - _queueMBean.deleteMessages(1L,1L); - assertTrue(_queueMBean.getMessageCount() == (messageCount - 1)); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - try - { - _queueMBean.viewMessageContent(1L); - fail("Message should no longer be on the queue"); - } - catch(Exception e) - { - - } - - //delete last message, leaving 2nd to 9th - _queueMBean.deleteMessages(10L,10L); - assertTrue(_queueMBean.getMessageCount() == (messageCount - 2)); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - try - { - _queueMBean.viewMessageContent(10L); - fail("Message should no longer be on the queue"); - } - catch(Exception e) - { - - } - - //delete remaining messages, leaving none - _queueMBean.deleteMessages(2L,9L); - assertTrue(_queueMBean.getMessageCount() == (0)); - assertTrue(_queueMBean.getReceivedMessageCount() == messageCount); - - //Ensure that the data has been removed from the Store - verifyBrokerState(); - } - - - // todo: collect to a general testing class -duplicated from Systest/MessageReturntest - private void verifyBrokerState() - { - - TestableMemoryMessageStore store = (TestableMemoryMessageStore) getVirtualHost().getMessageStore(); - - // Unlike MessageReturnTest there is no need for a delay as there this thread does the clean up. - - assertEquals("Store should have no messages:" + store.getMessageCount(), 0, store.getMessageCount()); - } - - public void testConsumerCount() throws AMQException - { - - assertTrue(getQueue().getActiveConsumerCount() == 0); - assertTrue(_queueMBean.getActiveConsumerCount() == 0); - - - InternalTestProtocolSession protocolSession = new InternalTestProtocolSession(getVirtualHost()); - - AMQChannel channel = new AMQChannel(protocolSession, 1, getMessageStore()); - protocolSession.addChannel(channel); - - Subscription subscription = - SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), protocolSession, new AMQShortString("test"), false, null, false, channel.getCreditManager()); - - getQueue().registerSubscription(subscription, false); - assertEquals(1,(int)_queueMBean.getActiveConsumerCount()); - - - SubscriptionFactory subscriptionFactory = SUBSCRIPTION_FACTORY; - Subscription s1 = subscriptionFactory.createSubscription(channel.getChannelId(), - protocolSession, - new AMQShortString("S1"), - false, - null, - true, - channel.getCreditManager()); - - Subscription s2 = subscriptionFactory.createSubscription(channel.getChannelId(), - protocolSession, - new AMQShortString("S2"), - false, - null, - true, - channel.getCreditManager()); - getQueue().registerSubscription(s1,false); - getQueue().registerSubscription(s2,false); - assertTrue(_queueMBean.getActiveConsumerCount() == 3); - assertTrue(_queueMBean.getConsumerCount() == 3); - - s1.close(); - assertEquals(2, (int) _queueMBean.getActiveConsumerCount()); - assertTrue(_queueMBean.getConsumerCount() == 3); - } - - public void testGeneralProperties() throws Exception - { - long maxQueueDepth = 1000; // in bytes - _queueMBean.setMaximumMessageCount(50000l); - _queueMBean.setMaximumMessageSize(2000l); - _queueMBean.setMaximumQueueDepth(maxQueueDepth); - - assertEquals("Max MessageCount not set",50000,_queueMBean.getMaximumMessageCount().longValue()); - assertEquals("Max MessageSize not set",2000, _queueMBean.getMaximumMessageSize().longValue()); - assertEquals("Max QueueDepth not set",maxQueueDepth, _queueMBean.getMaximumQueueDepth().longValue()); - - assertEquals("Queue Name does not match", new AMQShortString(getName()), _queueMBean.getName()); - assertFalse("AutoDelete should not be set.",_queueMBean.isAutoDelete()); - assertFalse("Queue should not be durable.",_queueMBean.isDurable()); - - //set+get exclusivity using the mbean, and also verify it is actually updated in the queue - _queueMBean.setExclusive(true); - assertTrue("Exclusive property should be true.",_queueMBean.isExclusive()); - assertTrue("Exclusive property should be true.", getQueue().isExclusive()); - _queueMBean.setExclusive(false); - assertFalse("Exclusive property should be false.",_queueMBean.isExclusive()); - assertFalse("Exclusive property should be false.", getQueue().isExclusive()); - } - - /** - * Tests view messages with two test messages. The first message is non-persistent, the second persistent - * and has timestamp/expiration. - * - */ - public void testViewMessages() throws Exception - { - sendMessages(1, false); - final Date msg2Timestamp = new Date(); - final Date msg2Expiration = new Date(msg2Timestamp.getTime() + 1000); - sendMessages(1, true, msg2Timestamp.getTime(), msg2Expiration.getTime()); - - final TabularData tab = _queueMBean.viewMessages(1l, 2l); - assertEquals("Unexpected number of rows in table", 2, tab.size()); - final Iterator rowItr = (Iterator) tab.values().iterator(); - - // Check row1 - final CompositeDataSupport row1 = rowItr.next(); - assertEquals("Message should have AMQ message id", 1l, row1.get(ManagedQueue.MSG_AMQ_ID)); - assertNotNull("Expected message header array", row1.get(ManagedQueue.MSG_HEADER)); - final Map row1Headers = headerArrayToMap((String[])row1.get(ManagedQueue.MSG_HEADER)); - assertEquals("Unexpected JMSPriority within header", "Non_Persistent", row1Headers.get("JMSDeliveryMode")); - assertEquals("Unexpected JMSTimestamp within header", "null", row1Headers.get("JMSTimestamp")); - assertEquals("Unexpected JMSExpiration within header", "null", row1Headers.get("JMSExpiration")); - - final CompositeDataSupport row2 = rowItr.next(); - assertEquals("Message should have AMQ message id", 2l, row2.get(ManagedQueue.MSG_AMQ_ID)); - assertNotNull("Expected message header array", row2.get(ManagedQueue.MSG_HEADER)); - final Map row2Headers = headerArrayToMap((String[])row2.get(ManagedQueue.MSG_HEADER)); - assertEquals("Unexpected JMSPriority within header", "Persistent", row2Headers.get("JMSDeliveryMode")); - final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(AMQQueueMBean.JMSTIMESTAMP_DATETIME_FORMAT); - assertEquals("Unexpected JMSTimestamp within header", msg2Timestamp, - simpleDateFormat.parse(row2Headers.get("JMSTimestamp"))); - assertEquals("Unexpected JMSExpiration within header", msg2Expiration, - simpleDateFormat.parse(row2Headers.get("JMSExpiration"))); - } - - public void testViewMessageWithIllegalStartEndRanges() throws Exception - { - try - { - _queueMBean.viewMessages(0L, 3L); - fail(); - } - catch (JMException ex) - { - // PASS - } - - try - { - _queueMBean.viewMessages(2L, 1L); - fail(); - } - catch (JMException ex) - { - // PASS - } - - try - { - _queueMBean.viewMessages(-1L, 1L); - fail(); - } - catch (JMException ex) - { - // PASS - } - - try - { - long end = Integer.MAX_VALUE; - end+=2; - _queueMBean.viewMessages(1L, end); - fail("Expected Exception due to oversized(> 2^31) message range"); - } - catch (JMException ex) - { - // PASS - } - } - - public void testViewMessageContent() throws Exception - { - final List sentMessages = sendMessages(1, true); - final Long id = sentMessages.get(0).getMessageId(); - - final CompositeData messageData = _queueMBean.viewMessageContent(id); - assertNotNull(messageData); - } - - public void testViewMessageContentWithUnknownMessageId() throws Exception - { - final List sentMessages = sendMessages(1, true); - final Long id = sentMessages.get(0).getMessageId(); - - try - { - _queueMBean.viewMessageContent(id + 1); - fail(); - } - catch (JMException ex) - { - // PASS - } - } - - public void testFlowControlProperties() throws Exception - { - assertTrue(_queueMBean.getCapacity() == 0); - assertTrue(_queueMBean.getFlowResumeCapacity() == 0); - assertFalse(_queueMBean.isFlowOverfull()); - - //capacity currently 0, try setting FlowResumeCapacity above this - try - { - _queueMBean.setFlowResumeCapacity(1L); - fail("Should have failed to allow setting FlowResumeCapacity above Capacity"); - } - catch (IllegalArgumentException ex) - { - //expected exception - assertTrue(_queueMBean.getFlowResumeCapacity() == 0); - } - - //add a message to the queue - sendMessages(1, true); - - //(FlowResume)Capacity currently 0, set both to 2 - _queueMBean.setCapacity(2L); - assertTrue(_queueMBean.getCapacity() == 2L); - _queueMBean.setFlowResumeCapacity(2L); - assertTrue(_queueMBean.getFlowResumeCapacity() == 2L); - - //Try setting Capacity below FlowResumeCapacity - try - { - _queueMBean.setCapacity(1L); - fail("Should have failed to allow setting Capacity below FlowResumeCapacity"); - } - catch (IllegalArgumentException ex) - { - //expected exception - assertTrue(_queueMBean.getCapacity() == 2); - } - - //create a channel and use it to exercise the capacity check mechanism - AMQChannel channel = new AMQChannel(getSession(), 1, getMessageStore()); - getQueue().checkCapacity(channel); - - assertTrue(_queueMBean.isFlowOverfull()); - assertTrue(channel.getBlocking()); - - //set FlowResumeCapacity to MESSAGE_SIZE and check queue is now underfull and channel unblocked - _queueMBean.setCapacity(MESSAGE_SIZE);//must increase capacity too - _queueMBean.setFlowResumeCapacity(MESSAGE_SIZE); - - assertFalse(_queueMBean.isFlowOverfull()); - assertFalse(channel.getBlocking()); - } - - public void testMaximumDeliveryCount() throws IOException - { - assertEquals("Unexpected default maximum delivery count", Integer.valueOf(0), _queueMBean.getMaximumDeliveryCount()); - } - - public void testViewAllMessages() throws Exception - { - final int messageCount = 5; - sendPersistentMessages(messageCount); - - - final TabularData messageTable = _queueMBean.viewMessages(1L, 5L); - assertNotNull("Message table should not be null", messageTable); - assertEquals("Unexpected number of rows", messageCount, messageTable.size()); - - - final Iterator rowIterator = messageTable.values().iterator(); - // Get its message ID - final CompositeDataSupport row1 = (CompositeDataSupport) rowIterator.next(); - final Long msgId = (Long) row1.get("AMQ MessageId"); - final Long queuePosition = (Long) row1.get("Queue Position"); - final Integer deliveryCount = (Integer) row1.get("Delivery Count"); - - assertNotNull("Row should have value for queue position", queuePosition); - assertNotNull("Row should have value for msgid", msgId); - assertNotNull("Row should have value for deliveryCount", deliveryCount); - } - - - @Override - public void setUp() throws Exception - { - super.setUp(); - - _queueMBean = new AMQQueueMBean(getQueue()); - } - - public void tearDown() - { - ApplicationRegistry.remove(); - } - - private void sendPersistentMessages(int messageCount) throws AMQException - { - sendMessages(messageCount, true); - assertEquals("Expected " + messageCount + " messages in the queue", messageCount, _queueMBean - .getMessageCount().intValue()); - } - - private List sendMessages(int messageCount, boolean persistent) throws AMQException - { - return sendMessages(messageCount, persistent, 0l, 0l); - } - - private List sendMessages(int messageCount, boolean persistent, long timestamp, long expiration) throws AMQException - { - final List sentMessages = new ArrayList(); - - for (int i = 0; i < messageCount; i++) - { - IncomingMessage currentMessage = createIncomingMessage(false, persistent, timestamp, expiration); - ArrayList qs = new ArrayList(); - qs.add(getQueue()); - currentMessage.enqueue(qs); - - // route header - MessageMetaData mmd = currentMessage.headersReceived(System.currentTimeMillis()); - - // Add the message to the store so we have something to test later - currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); - ContentChunk chunk = getSession().getMethodRegistry() - .getProtocolVersionMethodConverter() - .convertToContentChunk( - new ContentBody(new byte[(int) MESSAGE_SIZE])); - currentMessage.addContentBodyFrame(chunk); - currentMessage.getStoredMessage().addContent(0, ByteBuffer.wrap(chunk.getData())); - - AMQMessage m = new AMQMessage(currentMessage.getStoredMessage()); - for(BaseQueue q : currentMessage.getDestinationQueues()) - { - q.enqueue(m); - } - - sentMessages.add(m); - } - - return sentMessages; - } - - private IncomingMessage createIncomingMessage(final boolean immediate, boolean persistent, long timestamp, long expiration) throws AMQException - { - MessagePublishInfo publish = new MessagePublishInfo() - { - - public AMQShortString getExchange() - { - return null; - } - - public void setExchange(AMQShortString exchange) - { - } - - public boolean isImmediate() - { - return immediate; - } - - public boolean isMandatory() - { - return false; - } - - public AMQShortString getRoutingKey() - { - return null; - } - }; - - ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); - contentHeaderBody.setBodySize(MESSAGE_SIZE); // in bytes - final BasicContentHeaderProperties props = new BasicContentHeaderProperties(); - contentHeaderBody.setProperties(props); - props.setDeliveryMode((byte) (persistent ? 2 : 1)); - if (timestamp > 0) - { - props.setTimestamp(timestamp); - } - if (expiration > 0) - { - props.setExpiration(expiration); - } - IncomingMessage msg = new IncomingMessage(publish); - msg.setContentHeaderBody(contentHeaderBody); - return msg; - } - - /** - * - * Utility method to convert array of Strings in the form x = y into a - * map with key/value x => y. - * - */ - private Map headerArrayToMap(final String[] headerArray) - { - final Map headerMap = new HashMap(); - final List headerList = Arrays.asList(headerArray); - for (Iterator iterator = headerList.iterator(); iterator.hasNext();) - { - final String nameValuePair = iterator.next(); - final String[] nameValue = nameValuePair.split(" *= *", 2); - headerMap.put(nameValue[0], nameValue[1]); - } - return headerMap; - } - - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index afaa417415..d3eb61a544 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -29,13 +29,14 @@ import org.apache.qpid.server.configuration.QueueConfigType; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.virtualhost.VirtualHost; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -51,7 +52,6 @@ public class MockAMQQueue implements AMQQueue private AuthorizationHolder _authorizationHolder; private AMQSessionModel _exclusiveOwner; - private AMQShortString _owner; private List _bindings = new CopyOnWriteArrayList(); private boolean _autoDelete; @@ -98,7 +98,12 @@ public class MockAMQQueue implements AMQQueue return "[MockAMQQueue]"; } - }; + }; + } + + public long getUnackedMessageBytes() + { + return 0; } public ConfigStore getConfigStore() @@ -121,6 +126,16 @@ public class MockAMQQueue implements AMQQueue return 0; } + public long getTotalDequeueCount() + { + return 0; + } + + public long getTotalEnqueueCount() + { + return 0; + } + public int getBindingCountHigh() { return 0; @@ -219,12 +234,27 @@ public class MockAMQQueue implements AMQQueue public void registerSubscription(Subscription subscription, boolean exclusive) throws AMQException { - + } public void unregisterSubscription(Subscription subscription) throws AMQException { - + + } + + public Collection getConsumers() + { + return Collections.emptyList(); + } + + public void addSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + { + + } + + public void removeSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + { + } public int getConsumerCount() @@ -283,7 +313,7 @@ public class MockAMQQueue implements AMQQueue } public int delete() throws AMQException - { + { _deleted = true; return getMessageCount(); } @@ -356,21 +386,6 @@ public class MockAMQQueue implements AMQQueue return null; } - public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) - { - - } - - public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) - { - - } - - public void removeMessagesFromQueue(long fromMessageId, long toMessageId) - { - - } - public long getMaximumMessageSize() { return 0; @@ -378,7 +393,7 @@ public class MockAMQQueue implements AMQQueue public void setMaximumMessageSize(long value) { - + } public long getMaximumMessageCount() @@ -388,7 +403,7 @@ public class MockAMQQueue implements AMQQueue public void setMaximumMessageCount(long value) { - + } public long getMaximumQueueDepth() @@ -398,7 +413,7 @@ public class MockAMQQueue implements AMQQueue public void setMaximumQueueDepth(long value) { - + } public long getMaximumMessageAge() @@ -408,7 +423,7 @@ public class MockAMQQueue implements AMQQueue public void setMaximumMessageAge(long maximumMessageAge) { - + } public long getMinimumAlertRepeatGap() @@ -418,7 +433,7 @@ public class MockAMQQueue implements AMQQueue public void deleteMessageFromTop() { - + } public long clearQueue() @@ -429,7 +444,7 @@ public class MockAMQQueue implements AMQQueue public void checkMessageStatus() throws AMQException { - + } public Set getNotificationChecks() @@ -439,22 +454,22 @@ public class MockAMQQueue implements AMQQueue public void flushSubscription(Subscription sub) throws AMQException { - + } public void deliverAsync(Subscription sub) { - + } public void deliverAsync() { - + } public void stop() { - + } public boolean isExclusive() @@ -469,7 +484,7 @@ public class MockAMQQueue implements AMQQueue public void setAlternateExchange(Exchange exchange) { - + } public Map getArguments() @@ -481,11 +496,6 @@ public class MockAMQQueue implements AMQQueue { } - public ManagedObject getManagedObject() - { - return null; - } - public int compareTo(AMQQueue o) { return 0; @@ -503,7 +513,7 @@ public class MockAMQQueue implements AMQQueue public void setCapacity(long capacity) { - + } public long getFlowResumeCapacity() @@ -513,7 +523,7 @@ public class MockAMQQueue implements AMQQueue public void setFlowResumeCapacity(long flowResumeCapacity) { - + } public void configure(ConfigurationPlugin config) @@ -546,12 +556,6 @@ public class MockAMQQueue implements AMQQueue _exclusiveOwner = exclusiveOwner; } - - public String getResourceName() - { - return _name.toString(); - } - public boolean isOverfull() { return false; @@ -582,7 +586,7 @@ public class MockAMQQueue implements AMQQueue return 0; } - public void decrementUnackedMsgCount() + public void decrementUnackedMsgCount(QueueEntry queueEntry) { } @@ -599,7 +603,6 @@ public class MockAMQQueue implements AMQQueue public void setExclusive(boolean exclusive) { - } public int getMaximumDeliveryCount() @@ -611,11 +614,23 @@ public class MockAMQQueue implements AMQQueue { } - public void setAlternateExchange(String exchangeName) + public void visit(final QueueEntryVisitor visitor) + { + } + + @Override + public void setNotificationListener(NotificationListener listener) { } - public void visit(final Visitor visitor) + @Override + public void setDescription(String description) { } + + @Override + public String getDescription() + { + return null; + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/NotificationCheckTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/NotificationCheckTest.java new file mode 100644 index 0000000000..df2de7f0e0 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/NotificationCheckTest.java @@ -0,0 +1,106 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import static org.mockito.Matchers.contains; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import static org.apache.qpid.server.queue.NotificationCheck.MESSAGE_AGE_ALERT; +import static org.apache.qpid.server.queue.NotificationCheck.MESSAGE_COUNT_ALERT; +import static org.apache.qpid.server.queue.NotificationCheck.MESSAGE_SIZE_ALERT; +import static org.apache.qpid.server.queue.NotificationCheck.QUEUE_DEPTH_ALERT; + + +import junit.framework.TestCase; + +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.queue.AMQQueue.NotificationListener; + +public class NotificationCheckTest extends TestCase +{ + + private ServerMessage _message = mock(ServerMessage.class); + private AMQQueue _queue = mock(AMQQueue.class); + private NotificationListener _listener = mock(NotificationListener.class); + + public void testMessageCountAlertFires() throws Exception + { + when(_queue.getMaximumMessageCount()).thenReturn(1000l); + when(_queue.getMessageCount()).thenReturn(999, 1000, 1001); + + MESSAGE_COUNT_ALERT.notifyIfNecessary(_message, _queue, _listener); + verifyZeroInteractions(_listener); + + MESSAGE_COUNT_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(MESSAGE_COUNT_ALERT), eq(_queue), eq("1000: Maximum count on queue threshold (1000) breached.")); + + MESSAGE_COUNT_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(MESSAGE_COUNT_ALERT), eq(_queue), eq("1001: Maximum count on queue threshold (1000) breached.")); + } + + public void testMessageSizeAlertFires() throws Exception + { + when(_queue.getMaximumMessageSize()).thenReturn(1024l); + when(_message.getSize()).thenReturn(1023l, 1024l, 1025l); + + MESSAGE_SIZE_ALERT.notifyIfNecessary(_message, _queue, _listener); + verifyZeroInteractions(_listener); + + MESSAGE_SIZE_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(MESSAGE_SIZE_ALERT), eq(_queue), contains("1024b : Maximum message size threshold (1024) breached.")); + + MESSAGE_SIZE_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(MESSAGE_SIZE_ALERT), eq(_queue), contains("1025b : Maximum message size threshold (1024) breached.")); + } + + public void testMessageAgeAlertFires() throws Exception + { + long now = System.currentTimeMillis(); + when(_queue.getMaximumMessageAge()).thenReturn(1000l); + when(_queue.getOldestMessageArrivalTime()).thenReturn(now, now - 15000); + + MESSAGE_AGE_ALERT.notifyIfNecessary(_message, _queue, _listener); + verifyZeroInteractions(_listener); + + MESSAGE_AGE_ALERT.notifyIfNecessary(_message, _queue, _listener); + // Uses contains as first part of message is nondeterministic + verify(_listener).notifyClients(eq(MESSAGE_AGE_ALERT), eq(_queue), contains("s : Maximum age on queue threshold (1s) breached.")); + } + + public void testQueueDepthAlertFires() throws Exception + { + when(_queue.getMaximumQueueDepth()).thenReturn(1024l); + when(_queue.getQueueDepth()).thenReturn(1023l, 1024l, 2048l); + + QUEUE_DEPTH_ALERT.notifyIfNecessary(_message, _queue, _listener); + verifyZeroInteractions(_listener); + + QUEUE_DEPTH_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(QUEUE_DEPTH_ALERT), eq(_queue), eq("1Kb : Maximum queue depth threshold (1Kb) breached.")); + + QUEUE_DEPTH_ALERT.notifyIfNecessary(_message, _queue, _listener); + verify(_listener).notifyClients(eq(QUEUE_DEPTH_ALERT), eq(_queue), eq("2Kb : Maximum queue depth threshold (1Kb) breached.")); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index a8fad96063..d2fe529856 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -21,6 +21,13 @@ package org.apache.qpid.server.queue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Matchers.contains; +import static org.mockito.Matchers.eq; + import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.qpid.AMQException; @@ -79,7 +86,6 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void setExchange(AMQShortString exchange) { - //To change body of implemented methods use File | Settings | File Templates. } public boolean isImmediate() @@ -839,120 +845,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } } - /** - * Tests that dequeued message is not copied as part of invocation of - * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String)} - */ - public void testCopyMessagesWithDequeuedEntry() - { - int messageNumber = 4; - int dequeueMessageIndex = 1; - String anotherQueueName = "testQueue2"; - // put test messages into a test queue - enqueueGivenNumberOfMessages(_queue, messageNumber); - - // dequeue message - dequeueMessage(_queue, dequeueMessageIndex); - - // create another queue - SimpleAMQQueue queue = createQueue(anotherQueueName); - - // copy messages into another queue - _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName); - - // get messages on another queue - List entries = queue.getMessagesOnTheQueue(); - - // assert another queue entries - assertEquals(messageNumber - 1, entries.size()); - int expectedId = 0; - for (int i = 0; i < messageNumber - 1; i++) - { - Long id = ((AMQMessage)entries.get(i).getMessage()).getMessageId(); - if (i == dequeueMessageIndex) - { - assertFalse("Message with id " + dequeueMessageIndex - + " was dequeued and should not been copied into another queue!", - new Long(expectedId).equals(id)); - expectedId++; - } - assertEquals("Expected message with id " + expectedId + " but got message with id " + id, - new Long(expectedId), id); - expectedId++; - } - } - - /** - * Tests that dequeued message is not moved as part of invocation of - * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String)} - */ - public void testMovedMessagesWithDequeuedEntry() - { - int messageNumber = 4; - int dequeueMessageIndex = 1; - String anotherQueueName = "testQueue2"; - - // put messages into a test queue - enqueueGivenNumberOfMessages(_queue, messageNumber); - - // dequeue message - dequeueMessage(_queue, dequeueMessageIndex); - - // create another queue - SimpleAMQQueue queue = createQueue(anotherQueueName); - - // move messages into another queue - _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName); - - // get messages on another queue - List entries = queue.getMessagesOnTheQueue(); - - // assert another queue entries - assertEquals(messageNumber - 1, entries.size()); - int expectedId = 0; - for (int i = 0; i < messageNumber - 1; i++) - { - Long id = ((AMQMessage)entries.get(i).getMessage()).getMessageId(); - if (i == dequeueMessageIndex) - { - assertFalse("Message with id " + dequeueMessageIndex - + " was dequeued and should not been copied into another queue!", - new Long(expectedId).equals(id)); - expectedId++; - } - assertEquals("Expected message with id " + expectedId + " but got message with id " + id, - new Long(expectedId), id); - expectedId++; - } - } - - /** - * Tests that messages in given range including dequeued one are deleted - * from the queue on invocation of - * {@link SimpleAMQQueue#removeMessagesFromQueue(long, long)} - */ - public void testRemoveMessagesFromQueueWithDequeuedEntry() - { - int messageNumber = 4; - int dequeueMessageIndex = 1; - - // put messages into a test queue - enqueueGivenNumberOfMessages(_queue, messageNumber); - - // dequeue message - dequeueMessage(_queue, dequeueMessageIndex); - - // remove messages - _queue.removeMessagesFromQueue(0, messageNumber); - - // get queue entries - List entries = _queue.getMessagesOnTheQueue(); - - // assert queue entries - assertNotNull("Null is returned from getMessagesOnTheQueue", entries); - assertEquals("Queue should be empty", 0, entries.size()); - } /** * Tests that dequeued message on the top is not accounted and next message @@ -1096,7 +989,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that entry in dequeued state are not enqueued and not delivered to subscription */ - public void testEqueueDequeuedEntry() + public void testEnqueueDequeuedEntry() { // create a queue where each even entry is considered a dequeued SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("test"), false, @@ -1231,6 +1124,39 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); } + public void testNotificationFiredOnEnqueue() throws Exception + { + AMQQueue.NotificationListener listener = mock(AMQQueue.NotificationListener.class); + + _queue.setNotificationListener(listener); + _queue.setMaximumMessageCount(2); + + _queue.enqueue(createMessage(new Long(24))); + verifyZeroInteractions(listener); + + _queue.enqueue(createMessage(new Long(25))); + + verify(listener, atLeastOnce()).notifyClients(eq(NotificationCheck.MESSAGE_COUNT_ALERT), eq(_queue), contains("Maximum count on queue threshold")); + } + + public void testNotificationFiredAsync() throws Exception + { + AMQQueue.NotificationListener listener = mock(AMQQueue.NotificationListener.class); + + _queue.enqueue(createMessage(new Long(24))); + _queue.enqueue(createMessage(new Long(25))); + _queue.enqueue(createMessage(new Long(26))); + + _queue.setNotificationListener(listener); + _queue.setMaximumMessageCount(2); + + verifyZeroInteractions(listener); + + _queue.checkMessageStatus(); + + verify(listener, atLeastOnce()).notifyClients(eq(NotificationCheck.MESSAGE_COUNT_ALERT), eq(_queue), contains("Maximum count on queue threshold")); + } + /** * A helper method to create a queue with given name * diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java index f6675e917e..c0c55de92a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java @@ -32,6 +32,7 @@ import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; +import java.net.InetSocketAddress; import java.util.Collections; /** @@ -47,7 +48,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase protected void setUp() throws Exception { - _rmipa = new RMIPasswordAuthenticator(); + _rmipa = new RMIPasswordAuthenticator(new InetSocketAddress(5672)); _credentials = new String[] {USERNAME, PASSWORD}; } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java index 48e631a0f4..8ab32d9710 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.server.store; import static org.mockito.Matchers.any; @@ -78,7 +98,7 @@ public class DurableConfigurationStoreTest extends QpidTestCase _exchangeId = UUIDGenerator.generateUUID(); _storeName = getName(); - _storePath = TMP_FOLDER + "/" + _storeName; + _storePath = TMP_FOLDER + File.separator + _storeName; FileUtils.delete(new File(_storePath), true); setTestSystemProperty("QPID_WORK", TMP_FOLDER); _configuration = mock(Configuration.class); @@ -172,7 +192,7 @@ public class DurableConfigurationStoreTest extends QpidTestCase _store.createQueue(queue); reopenStore(); - verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, null); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, null, null); } public void testCreateQueueAMQQueueFieldTable() throws Exception @@ -186,10 +206,29 @@ public class DurableConfigurationStoreTest extends QpidTestCase _store.createQueue(queue, arguments); reopenStore(); - verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, arguments); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, arguments, null); } - public void testUpdateQueue() throws Exception + public void testCreateQueueAMQQueueWithAlternateExchange() throws Exception + { + Exchange alternateExchange = createTestAlternateExchange(); + + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true, alternateExchange); + _store.createQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, null, alternateExchange.getId()); + } + + private Exchange createTestAlternateExchange() + { + UUID exchUuid = UUID.randomUUID(); + Exchange alternateExchange = mock(Exchange.class); + when(alternateExchange.getId()).thenReturn(exchUuid); + return alternateExchange; + } + + public void testUpdateQueueExclusivity() throws Exception { // create queue AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); @@ -204,7 +243,26 @@ public class DurableConfigurationStoreTest extends QpidTestCase _store.updateQueue(queue); reopenStore(); - verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", false, arguments); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", false, arguments, null); + } + + public void testUpdateQueueAlternateExchange() throws Exception + { + // create queue + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map attributes = new HashMap(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + // update the queue to have exclusive=false + Exchange alternateExchange = createTestAlternateExchange(); + queue = createTestQueue(getName(), getName() + "Owner", false, alternateExchange); + _store.updateQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", false, arguments, alternateExchange.getId()); } public void testRemoveQueue() throws Exception @@ -221,10 +279,15 @@ public class DurableConfigurationStoreTest extends QpidTestCase _store.removeQueue(queue); reopenStore(); verify(_queueRecoveryHandler, never()).queue(any(UUID.class), anyString(), anyString(), anyBoolean(), - any(FieldTable.class)); + any(FieldTable.class), any(UUID.class)); } private AMQQueue createTestQueue(String queueName, String queueOwner, boolean exclusive) throws AMQStoreException + { + return createTestQueue(queueName, queueOwner, exclusive, null); + } + + private AMQQueue createTestQueue(String queueName, String queueOwner, boolean exclusive, Exchange alternateExchange) throws AMQStoreException { AMQQueue queue = mock(AMQQueue.class); when(queue.getName()).thenReturn(queueName); @@ -232,6 +295,7 @@ public class DurableConfigurationStoreTest extends QpidTestCase when(queue.getOwner()).thenReturn(AMQShortString.valueOf(queueOwner)); when(queue.isExclusive()).thenReturn(exclusive); when(queue.getId()).thenReturn(_queueId); + when(queue.getAlternateExchange()).thenReturn(alternateExchange); return queue; } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java index 64048d294b..93f3701c85 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -99,7 +99,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase { super.setUp(); - String storePath = System.getProperty("QPID_WORK") + "/" + getName(); + String storePath = System.getProperty("QPID_WORK") + File.separator + getName(); _config = new PropertiesConfiguration(); _config.addProperty("store.class", getTestProfileMessageStoreClassName()); @@ -267,15 +267,9 @@ public class MessageStoreTest extends InternalBrokerBaseCase //Validate normally expected properties of Queues/Topics validateDurableQueueProperties(); - //Update the durable exclusive queue's exclusivity and verify it is persisted and recovered correctly + //Update the durable exclusive queue's exclusivity setQueueExclusivity(false); validateQueueExclusivityProperty(false); - - //Reload the Virtualhost to recover the queues again - reloadVirtualHost(); - - //verify the change was persisted and recovered correctly - validateQueueExclusivityProperty(false); } /** diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java index c6ef35d255..24e8b591e0 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java @@ -147,6 +147,12 @@ public class OperationalLoggingListenerTest extends TestCase { _eventManager.addEventListener(eventListener, events); } + + @Override + public String getStoreType() + { + return "TEST"; + } } private static class TestActor implements LogActor diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java index 5ba9c0c015..0aa0569b07 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -1,5 +1,3 @@ -package org.apache.qpid.server.subscription; - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -21,16 +19,24 @@ package org.apache.qpid.server.subscription; * */ +package org.apache.qpid.server.subscription; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.QueueEntry.SubscriptionAcquiredState; +import org.apache.qpid.server.stats.StatisticsCounter; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -76,19 +82,9 @@ public class MockSubscription implements Subscription _state = State.CLOSED; } - public boolean filtersMessages() - { - return false; - } - - public AMQChannel getChannel() - { - return null; - } - - public AMQShortString getConsumerTag() + public String getConsumerName() { - return tag; + return tag == null ? null : tag.asString(); } public long getSubscriptionID() @@ -121,11 +117,36 @@ public class MockSubscription implements Subscription return false; } + public long getBytesOut() + { + return 0; // TODO - Implement + } + + public long getMessagesOut() + { + return 0; // TODO - Implement + } + + public long getUnacknowledgedBytes() + { + return 0; // TODO - Implement + } + + public long getUnacknowledgedMessages() + { + return 0; // TODO - Implement + } + public AMQQueue getQueue() { return queue; } + public AMQSessionModel getSessionModel() + { + return new MockSessionModel(); + } + public boolean trySendLock() { return _stateChangeLock.tryLock(); @@ -154,11 +175,6 @@ public class MockSubscription implements Subscription return _isActive ; } - public void confirmAutoClose() - { - - } - public void set(String key, Object value) { } @@ -173,11 +189,6 @@ public class MockSubscription implements Subscription return false; } - public boolean isBrowser() - { - return false; - } - public boolean isClosed() { return _closed; @@ -207,10 +218,6 @@ public class MockSubscription implements Subscription _stateChangeLock.unlock(); } - public void resend(QueueEntry entry) throws AMQException - { - } - public void onDequeue(QueueEntry queueEntry) { } @@ -232,7 +239,6 @@ public class MockSubscription implements Subscription messages.add(entry); } - @Override public void flushBatched() { @@ -249,7 +255,7 @@ public class MockSubscription implements Subscription } public void setNoLocal(boolean noLocal) - { + { } public void setStateListener(StateListener listener) @@ -285,4 +291,259 @@ public class MockSubscription implements Subscription { _isActive = isActive; } + + private static class MockSessionModel implements AMQSessionModel + { + + @Override + public int compareTo(AMQSessionModel o) + { + return 0; + } + + @Override + public UUID getId() + { + return null; + } + + @Override + public AMQConnectionModel getConnectionModel() + { + return new MockConnectionModel(); + } + + @Override + public String getClientID() + { + return null; + } + + @Override + public void close() throws AMQException + { + } + + @Override + public LogSubject getLogSubject() + { + return null; + } + + @Override + public void checkTransactionStatus(long openWarn, long openClose, + long idleWarn, long idleClose) throws AMQException + { + } + + @Override + public void block(AMQQueue queue) + { + } + + @Override + public void unblock(AMQQueue queue) + { + } + + @Override + public void block() + { + } + + @Override + public void unblock() + { + } + + @Override + public boolean getBlocking() + { + return false; + } + + @Override + public boolean onSameConnection(InboundMessage inbound) + { + return false; + } + + @Override + public int getUnacknowledgedMessageCount() + { + return 0; + } + + @Override + public Long getTxnCount() + { + return null; + } + + @Override + public Long getTxnStart() + { + return null; + } + + @Override + public Long getTxnCommits() + { + return null; + } + + @Override + public Long getTxnRejects() + { + return null; + } + + @Override + public int getChannelId() + { + return 0; + } + } + + private static class MockConnectionModel implements AMQConnectionModel + { + @Override + public void initialiseStatistics() + { + } + + @Override + public void registerMessageReceived(long messageSize, long timestamp) + { + } + + @Override + public void registerMessageDelivered(long messageSize) + { + } + + @Override + public StatisticsCounter getMessageDeliveryStatistics() + { + return null; + } + + @Override + public StatisticsCounter getMessageReceiptStatistics() + { + return null; + } + + @Override + public StatisticsCounter getDataDeliveryStatistics() + { + return null; + } + + @Override + public StatisticsCounter getDataReceiptStatistics() + { + return null; + } + + @Override + public void resetStatistics() + { + + } + + @Override + public UUID getId() + { + return null; + } + + @Override + public void close(AMQConstant cause, String message) + throws AMQException + { + } + + @Override + public void closeSession(AMQSessionModel session, AMQConstant cause, + String message) throws AMQException + { + } + + @Override + public long getConnectionId() + { + return 0; + } + + @Override + public List getSessionModels() + { + return null; + } + + @Override + public void block() + { + } + + @Override + public void unblock() + { + } + + @Override + public LogSubject getLogSubject() + { + return null; + } + + @Override + public String getUserName() + { + return null; + } + + @Override + public boolean isSessionNameUnique(byte[] name) + { + return false; + } + + @Override + public String getRemoteAddressString() + { + return "remoteAddress:1234"; + } + + @Override + public String getClientId() + { + return null; + } + + @Override + public String getClientVersion() + { + return null; + } + + @Override + public String getPrincipalAsString() + { + return null; + } + + @Override + public long getSessionCountLimit() + { + return 0; + } + + @Override + public long getLastIoTime() + { + return 0; + } + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java deleted file mode 100644 index b0b81355ac..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/transport/ServerConnectionMBeanTest.java +++ /dev/null @@ -1,231 +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.transport; - -import org.apache.qpid.management.common.mbeans.ManagedConnection; -import org.apache.qpid.server.configuration.MockConnectionConfig; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.util.InternalBrokerBaseCase; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.transport.Binary; -import org.apache.qpid.transport.Connection; -import org.apache.qpid.transport.Session; - -import javax.management.JMException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.TabularData; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; - -public class ServerConnectionMBeanTest extends InternalBrokerBaseCase -{ - private ServerConnection _serverConnection; - private ServerSessionMock _serverSession; - private ServerConnectionMBean _mbean; - private List _sessions = new ArrayList(); - - @Override - public void setUp() throws Exception - { - super.setUp(); - - final VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); - _serverConnection = new ServerConnection(1) - { - protected Collection getChannels() - { - return _sessions; - } - public Session getSession(int channelId) - { - for(Session session : _sessions) - { - if (session.getChannel() == channelId) - { - return session; - } - } - return null; - } - @Override - public AtomicLong getLastIoTime() - { - return new AtomicLong(1); - } - }; - final MockConnectionConfig config = new MockConnectionConfig(UUID.randomUUID(), null, null, - false, 1, vhost, "address", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, - "authid", "remoteProcessName", new Integer(1967), new Integer(1970), vhost.getConfigStore(), Boolean.FALSE); - _serverConnection.setConnectionConfig(config); - _serverConnection.setVirtualHost(vhost); - _serverConnection.setConnectionDelegate(new ServerConnectionDelegate(getRegistry(), "", getRegistry().getAuthenticationManager(new InetSocketAddress(5672)))); - _serverSession = new ServerSessionMock(_serverConnection, 1); - _mbean = (ServerConnectionMBean) _serverConnection.getManagedObject(); - } - - public void testChannels() throws Exception - { - // check the channel count is correct - TabularData tabularData = _mbean.channels(); - - int channelCount = tabularData.size(); - assertEquals("Unexpected number of channels",1,channelCount); - _sessions.add(new ServerSession(_serverConnection, new ServerSessionDelegate(), - new Binary(getName().getBytes()), 2 , _serverConnection.getConfig())); - - channelCount = _mbean.channels().size(); - assertEquals("Unexpected number of channels",2,channelCount); - - final CompositeData chanresult = tabularData.get(new Integer[]{1}); - assertNotNull(chanresult); - assertEquals("Unexpected channel id", new Integer(1),(Integer)chanresult.get(ManagedConnection.CHAN_ID)); - assertNull("Unexpected default queue", chanresult.get(ManagedConnection.DEFAULT_QUEUE)); - assertFalse("Unexpected transactional flag", (Boolean)chanresult.get(ManagedConnection.TRANSACTIONAL)); - assertFalse("Flow should have been blocked", (Boolean)chanresult.get(ManagedConnection.FLOW_BLOCKED)); - assertEquals("Unexpected unack'd count", new Integer(1967), (Integer)chanresult.get(ManagedConnection.UNACKED_COUNT)); - } - - public void testMaxChannels() throws Exception - { - _serverConnection.getConnectionDelegate().setChannelMax(10001); - assertEquals("Max channels not got correctly", new Long(10001), _mbean.getMaximumNumberOfChannels()); - } - - public void testRollback() throws Exception - { - _mbean.rollbackTransactions(1); - assertFalse("Rollback performed despite not being transacted", _serverSession.isRolledback()); - - _serverSession.setTransactional(true); - _mbean.rollbackTransactions(1); - assertTrue("Rollback not performed", _serverSession.isRolledback()); - - try - { - _mbean.rollbackTransactions(2); - fail("Exception expected"); - } - catch (JMException jme) - { - //pass - } - } - - public void testCommit() throws Exception - { - _mbean.commitTransactions(1); - assertFalse("Commit performed despite not being transacted", _serverSession.isCommitted()); - - _serverSession.setTransactional(true); - _mbean.commitTransactions(1); - assertTrue("Commit not performed", _serverSession.isCommitted()); - - try - { - _mbean.commitTransactions(2); - fail("Exception expected"); - } - catch (JMException jme) - { - //pass - } - } - - public void testGetName() - { - assertEquals("Unexpected Object Instance Name", "\"address\"", _mbean.getObjectInstanceName()); - } - - public void testEnableStatistics() - { - assertFalse("Unexpected statistics enable flag", _mbean.isStatisticsEnabled()); - _mbean.setStatisticsEnabled(true); - assertTrue("Unexpected statistics enable flag", _mbean.isStatisticsEnabled()); - } - - public void testLastIOTime() - { - assertEquals("Unexpected last IO time", new Date(1), _mbean.getLastIoTime()); - } - - private class ServerSessionMock extends ServerSession - { - private int _channelId = 0; - private boolean _committed = false; - private boolean _rolledback = false; - private boolean _transacted = false; - - ServerSessionMock(Connection connection, int channelId) - { - super(connection, new ServerSessionDelegate(), new Binary(String.valueOf(channelId).getBytes()), 1 , _serverConnection.getConfig()); - _channelId = channelId; - _sessions.add(this); - } - - public int getChannel() - { - return _channelId; - } - - @Override - public void commit() - { - _committed = true; - } - - @Override - public void rollback() - { - _rolledback = true; - } - - public boolean isCommitted() - { - return _committed; - } - - public boolean isRolledback() - { - return _rolledback; - } - - @Override - public int getUnacknowledgedMessageCount() - { - return 1967; - } - - public boolean isTransactional() - { - return _transacted; - } - - public void setTransactional(boolean transacted) - { - _transacted = transacted; - } - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java index af49238998..0221f3d509 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java @@ -124,6 +124,12 @@ class MockStoreTransaction implements Transaction storeTransaction.setState(TransactionState.STARTED); return storeTransaction; } + + @Override + public String getStoreType() + { + return "TEST"; + } }; } } \ No newline at end of file diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java index 9bd69e3889..a64ab620ab 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -21,6 +21,8 @@ package org.apache.qpid.server.util; import java.net.SocketAddress; +import java.util.Collections; +import java.util.Map; import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.configuration.ServerConfiguration; @@ -101,6 +103,17 @@ public class TestApplicationRegistry extends ApplicationRegistry { return pdam; } + + @Override + public Map getAvailableAuthenticationManagers() + { + return Collections.singletonMap(pdam.getClass().getName(), pdam); + } + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) + { + } }; } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java index 58c7625ad6..df31845798 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java @@ -32,7 +32,6 @@ import org.apache.qpid.server.connection.IConnectionRegistry; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; @@ -141,11 +140,6 @@ public class MockVirtualHost implements VirtualHost return 0; } - public ManagedObject getManagedObject() - { - return null; - } - public MessageStore getMessageStore() { return null; @@ -257,11 +251,6 @@ public class MockVirtualHost implements VirtualHost } - public boolean isStatisticsEnabled() - { - return false; - } - public void registerMessageDelivered(long messageSize) { @@ -277,11 +266,6 @@ public class MockVirtualHost implements VirtualHost } - public void setStatisticsEnabled(boolean enabled) - { - - } - public State getState() { return State.ACTIVE; diff --git a/qpid/java/build.deps b/qpid/java/build.deps index 5b0d23f8cc..ae49c85195 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -47,6 +47,18 @@ slf4j-log4j=lib/required/slf4j-log4j12-1.6.4.jar xalan=lib/required/xalan-2.7.0.jar +jetty=lib/required/jetty-server-7.6.3.v20120416.jar +jetty-continuation=lib/required/jetty-continuation-7.6.3.v20120416.jar +jetty-security=lib/required/jetty-security-7.6.3.v20120416.jar +jetty-util=lib/required/jetty-util-7.6.3.v20120416.jar +jetty-io=lib/required/jetty-io-7.6.3.v20120416.jar +jetty-http=lib/required/jetty-http-7.6.3.v20120416.jar +jetty-servlet=lib/required/jetty-servlet-7.6.3.v20120416.jar +jetty-websocket=lib/required/jetty-websocket-7.6.3.v20120416.jar +servlet-api=${geronimo-servlet} + +dojo=lib/required/dojo-war-1.7.2.war + felix-main=lib/required/org.apache.felix.main-2.0.5.jar felix.libs=${felix-main} @@ -65,8 +77,9 @@ amqp-1-0-client-jms.libs=${geronimo-jms} tools.libs=${commons-configuration.libs} ${log4j} broker.libs=${commons-cli} ${commons-logging} ${log4j} ${slf4j-log4j} \ ${xalan} ${felix.libs} ${derby-db} ${commons-configuration.libs} \ - ${jackson-core} ${jackson-mapper} + ${jackson-core} ${jackson-mapper} ${jetty} ${jetty-continuation} ${jetty-security} ${jetty-http} ${jetty-io} ${jetty-servlet} ${jetty-util} ${servlet-api} ${jetty-websocket} +broker-plugins-management.libs=${jetty} ${jetty-continuation} ${jetty-security} ${jetty-http} ${jetty-io} ${jetty-servlet} ${jetty-util} ${servlet-api} ${jackson-core} ${jackson-mapper} broker-plugins.libs=${felix.libs} ${log4j} ${commons-configuration.libs} test.libs=${slf4j-log4j} ${log4j} ${junit} ${slf4j-api} ${mockito-all} @@ -154,6 +167,9 @@ bdb-je=lib/bdbstore/je-5.0.55.jar bdbstore.libs=${bdb-je} bdbstore.test.libs=${test.libs} +bdbstore-jmx.libs=${bdb-je} +bdbstore-jmx.test.libs=${test.libs} + # optional perftests-visualisation-jfc module deps jfreechart.jar=lib/jfree/jfreechart-1.0.13.jar jcommon.jar=lib/jfree/jcommon-1.0.16.jar diff --git a/qpid/java/build.xml b/qpid/java/build.xml index 5ac4b03a03..a41cff4d78 100644 --- a/qpid/java/build.xml +++ b/qpid/java/build.xml @@ -27,7 +27,7 @@ - + diff --git a/qpid/java/common.xml b/qpid/java/common.xml index ab71642053..b5ae5679d0 100644 --- a/qpid/java/common.xml +++ b/qpid/java/common.xml @@ -277,8 +277,8 @@ - - + diff --git a/qpid/java/ivy.retrieve.xml b/qpid/java/ivy.retrieve.xml index 8e2806a509..98d3d3785d 100644 --- a/qpid/java/ivy.retrieve.xml +++ b/qpid/java/ivy.retrieve.xml @@ -60,6 +60,15 @@ + + + + + + + + +